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">
<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"/>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<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 fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<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="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"/>
<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" 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" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
</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';
import type { Address as TAddress } from 'types/api/address';
import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import { ADDRESS_COUNTERS } from 'stubs/address';
import AddressCounterItem from 'ui/address/details/AddressCounterItem';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
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 AddressNameInfo from './details/AddressNameInfo';
......@@ -102,9 +98,13 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
hint="Transaction and address of creation"
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>
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon/>
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
</DetailsInfoItem>
) }
{ data.is_contract && data.implementation_address && (
......@@ -113,14 +113,11 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
hint="Implementation address of the proxy contract"
columnGap={ 1 }
>
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: data.implementation_address } }) } overflow="hidden">
{ data.implementation_name || <HashStringShortenDynamic hash={ data.implementation_address }/> }
</LinkInternal>
{ data.implementation_name && (
<Text variant="secondary" overflow="hidden">
<HashStringShortenDynamic hash={ `(${ data.implementation_address })` }/>
</Text>
) }
<AddressEntity
address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
isLoading={ addressQuery.isPlaceholderData }
noIcon
/>
</DetailsInfoItem>
) }
<AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
......
......@@ -22,11 +22,11 @@ import TOKEN_TYPE from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenLogo from 'ui/shared/TokenLogo';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
......@@ -227,19 +227,20 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
address: tokenFilter || '',
name: '',
icon_url: '',
symbol: '',
}), [ tokenFilter ]);
const tokenFilterComponent = tokenFilter && (
<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>
<Flex alignItems="center" py={ 1 }>
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/>
<TokenEntity.Icon token={ tokenData } isLoading={ isPlaceholderData }/>
{ isMobile ? <HashStringShorten hash={ tokenFilter }/> : tokenFilter }
<Tooltip label="Reset filter">
<Flex>
<Icon
as={ crossIcon }
boxSize={ 6 }
boxSize={ 5 }
ml={ 1 }
color={ resetTokenIconColor }
cursor="pointer"
......
......@@ -47,6 +47,7 @@ base.describe('base view', () => {
base.use({ viewport: configs.viewport.xl });
base('', async() => {
base.slow();
await expect(component).toHaveScreenshot();
});
});
......
......@@ -12,10 +12,8 @@ import dayjs from 'lib/date/dayjs';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
......@@ -107,9 +105,14 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
const decoded = data.decoded_constructor_args
.map(([ value, { name, type } ], index) => {
const valueEl = type === 'address' ?
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: value } }) }>{ value }</LinkInternal> :
<span>{ value }</span>;
const valueEl = type === 'address' ? (
<AddressEntity
address={{ hash: value }}
noIcon
display="inline-flex"
maxW="100%"
/>
) : <span>{ value }</span>;
return (
<Box key={ index }>
<span>Arg [{ index }] { name || '' } ({ type }): </span>
......@@ -171,10 +174,12 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
{ !data?.is_verified && data?.verified_twin_address_hash && !data?.minimal_proxy_address_hash && (
<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>
<Address>
<AddressIcon 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 }/>
</Address>
<AddressEntity
address={{ hash: data.verified_twin_address_hash, is_contract: true, implementation_name: null }}
truncation="constant"
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>
<LinkInternal href={ route({ pathname: '/address/[hash]/contract-verification', query: { hash: addressHash || '' } }) }>
Verify & Publish
......@@ -185,10 +190,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
{ data?.minimal_proxy_address_hash && (
<Alert status="warning" flexWrap="wrap" whiteSpace="pre-wrap">
<span>Minimal Proxy Contract for </span>
<Address>
<AddressIcon 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 }/>
</Address>
<AddressEntity
address={{ hash: data.minimal_proxy_address_hash, is_contract: true, implementation_name: null }}
truncation="constant"
fontSize="sm"
fontWeight="500"
noCopy
/>
<span>. </span>
<Box>
<Link href="https://eips.ethereum.org/EIPS/eip-1167">EIP-1167</Link>
......
......@@ -4,8 +4,7 @@ import React from 'react';
import { useAccount, useDisconnect } from 'wagmi';
import useIsMobile from 'lib/hooks/useIsMobile';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
const ContractConnectWallet = () => {
const { open, isOpen } = useWeb3Modal();
......@@ -47,8 +46,12 @@ const ContractConnectWallet = () => {
<Flex columnGap={ 3 } rowGap={ 3 } alignItems={{ base: 'flex-start', lg: 'center' }} flexDir={{ base: 'column', lg: 'row' }}>
<Flex alignItems="center">
<span>Connected to </span>
<AddressIcon address={{ hash: address, is_contract: false, implementation_name: null }} mx={ 2 }/>
<AddressLink type="address" fontWeight={ 600 } hash={ address } truncation={ isMobile ? 'constant' : 'dynamic' }/>
<AddressEntity
address={{ hash: address }}
truncation={ isMobile ? 'constant' : 'dynamic' }
fontWeight={ 600 }
ml={ 2 }
/>
</Flex>
<Button onClick={ handleDisconnect } size="sm" variant="outline">Disconnect</Button>
</Flex>
......
......@@ -24,10 +24,7 @@ import arrowIcon from 'icons/arrows/east-mini.svg';
import iconWarning from 'icons/status/warning.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
className?: string;
......@@ -38,11 +35,13 @@ const Item = (data: SmartContractExternalLibrary) => {
return (
<Flex flexDir="column" py={ 2 } w="100%" rowGap={ 1 }>
<Box>{ data.name }</Box>
<Address>
<AddressIcon 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' }}/>
<CopyToClipboard text={ data.address_hash }/>
</Address>
<AddressEntity
address={{ hash: data.address_hash, is_contract: true, implementation_name: null }}
query={{ tab: 'contract' }}
fontSize="sm"
fontWeight="500"
target="_blank"
/>
</Flex>
);
};
......
import { Flex } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { Address as TAddress } from 'types/api/address';
import { getResourceKey } from 'lib/api/useApiQuery';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
hash: string | undefined;
......@@ -22,10 +22,10 @@ const ContractImplementationAddress = ({ hash }: Props) => {
}
return (
<Address whiteSpace="pre-wrap" flexWrap="wrap" mb={ 6 }>
<span>Implementation address: </span>
<AddressLink type="address" hash={ data.implementation_address }/>
</Address>
<Flex mb={ 6 } flexWrap="wrap" columnGap={ 2 }>
<span>Implementation address:</span>
<AddressEntity address={{ hash: data.implementation_address, is_contract: true }} noIcon noCopy/>
</Flex>
);
};
......
......@@ -7,9 +7,7 @@ import type { SmartContractMethodOutput } from 'types/api/contract';
import config from 'configs/app';
import { WEI } from 'lib/consts';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
function castValueToString(value: number | string | boolean | bigint | undefined): string {
switch (typeof value) {
......@@ -49,10 +47,10 @@ const ContractMethodStatic = ({ data }: Props) => {
const content = (() => {
if (typeof data.value === 'string' && data.type === 'address' && data.value) {
return (
<Address>
<AddressLink type="address" hash={ data.value }/>
<CopyToClipboard text={ data.value }/>
</Address>
<AddressEntity
address={{ hash: data.value }}
noIcon
/>
);
}
......
......@@ -10,7 +10,6 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
data: Pick<Address, 'block_number_balance_updated_at' | 'coin_balance' | 'hash' | 'exchange_rate'>;
......@@ -64,11 +63,6 @@ const AddressBalance = ({ data, isLoading }: Props) => {
handler: handleNewCoinBalanceMessage,
});
const tokenData = React.useMemo(() => ({
name: config.chain.currency.name || '',
icon_url: '',
}), [ ]);
return (
<DetailsInfoItem
title="Balance"
......@@ -77,13 +71,6 @@ const AddressBalance = ({ data, isLoading }: Props) => {
alignItems="flex-start"
isLoading={ isLoading }
>
<TokenLogo
data={ tokenData }
boxSize={ 5 }
mr={ 2 }
fontSize="sm"
isLoading={ isLoading }
/>
<CurrencyValue
value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate }
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { Address } from 'types/api/address';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
interface Props {
data: Pick<Address, 'name' | 'token' | 'is_contract'>;
......@@ -19,7 +19,12 @@ const AddressNameInfo = ({ data, isLoading }: Props) => {
hint="Token name and symbol"
isLoading={ isLoading }
>
<TokenSnippet data={ data.token } isLoading={ isLoading } hideIcon/>
<TokenEntity
token={ data.token }
isLoading={ isLoading }
noIcon
noCopy
/>
</DetailsInfoItem>
);
}
......
......@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg';
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 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 TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
......@@ -40,7 +37,7 @@ const TxInternalsListItem = ({
const toData = to ? to : createdContract;
const isOut = Boolean(currentAddress && currentAddress === from.hash);
const isIn = Boolean(currentAddress && currentAddress === to?.hash);
const isIn = Boolean(currentAddress && currentAddress === toData?.hash);
return (
<ListItemMobile rowGap={ 3 }>
......@@ -70,21 +67,25 @@ const TxInternalsListItem = ({
/>
</HStack>
<Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ isOut } isLoading={ isLoading }/>
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address>
<AddressEntity
address={ from }
isLoading={ isLoading }
noLink={ isOut }
noCopy={ isOut }
width="calc((100% - 48px) / 2)"
/>
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
}
{ toData && (
<Address width="calc((100% - 48px) / 2)">
<AddressIcon address={ toData } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isDisabled={ isIn } isLoading={ isLoading }/>
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> }
</Address>
<AddressEntity
address={ toData }
isLoading={ isLoading }
noLink={ isIn }
noCopy={ isIn }
width="calc((100% - 48px) / 2)"
/>
) }
</Box>
<HStack spacing={ 3 }>
......
......@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
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 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 TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
......@@ -39,7 +36,7 @@ const AddressIntTxsTableItem = ({
const toData = to ? to : createdContract;
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);
......@@ -81,34 +78,27 @@ const AddressIntTxsTableItem = ({
/>
</Td>
<Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink
type="address"
ml={ 2 }
fontWeight="500"
hash={ from.hash }
alias={ from.name }
flexGrow={ 1 }
isDisabled={ isOut }
<AddressEntity
address={ from }
isLoading={ isLoading }
noLink={ isOut }
noCopy={ isOut }
/>
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address>
</Td>
<Td px={ 0 } verticalAlign="middle">
{ (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 }/>
}
</Td>
<Td verticalAlign="middle">
{ toData && (
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ toData } isLoading={ isLoading }/>
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isDisabled={ isIn } isLoading={ isLoading }/>
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> }
</Address>
<AddressEntity
address={ toData }
isLoading={ isLoading }
noLink={ isIn }
noCopy={ isIn }
/>
) }
</Td>
<Td isNumeric verticalAlign="middle">
......
......@@ -4,7 +4,7 @@ import React from 'react';
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 type { TokenEnhancedData } from '../utils/tokenUtils';
......@@ -68,7 +68,13 @@ const TokenSelectItem = ({ data }: Props) => {
href={ url }
>
<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> }
</Flex>
<Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap">
......
......@@ -5,17 +5,14 @@ import type { AddressTokenBalance } from 'types/api/address';
import getCurrencyValue from 'lib/getCurrencyValue';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
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 TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean};
const ERC20TokensListItem = ({ token, value, isLoading }: Props) => {
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
const {
valueStr: tokenQuantity,
usd: tokenValue,
......@@ -24,12 +21,21 @@ const ERC20TokensListItem = ({ token, value, isLoading }: Props) => {
return (
<ListItemMobile rowGap={ 2 }>
<Flex alignItems="center" width="100%">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontWeight="700" hash={ token.address } type="token" alias={ tokenString } isLoading={ isLoading }/>
<TokenEntity
token={ token }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
/>
</Flex>
<Flex alignItems="center" pl={ 8 }>
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/>
<CopyToClipboard text={ token.address } isLoading={ isLoading }/>
<AddressEntity
address={{ hash: token.address }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Flex>
{ token.exchange_rate !== undefined && token.exchange_rate !== null && (
......
......@@ -5,9 +5,8 @@ import type { AddressTokenBalance } from 'types/api/address';
import getCurrencyValue from 'lib/getCurrencyValue';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import TokenLogo from 'ui/shared/TokenLogo';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
type Props = AddressTokenBalance & { isLoading: boolean };
......@@ -17,8 +16,6 @@ const ERC20TokensTableItem = ({
isLoading,
}: Props) => {
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
const {
valueStr: tokenQuantity,
usd: tokenValue,
......@@ -27,17 +24,21 @@ const ERC20TokensTableItem = ({
return (
<Tr>
<Td verticalAlign="middle">
<Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontWeight="700" hash={ token.address } type="token" alias={ tokenString } isLoading={ isLoading }/>
</Flex>
<TokenEntity
token={ token }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
/>
</Td>
<Td verticalAlign="middle">
<Flex alignItems="center" width="150px" justifyContent="space-between">
<Flex alignItems="center">
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/>
<CopyToClipboard text={ token.address } isLoading={ isLoading }/>
</Flex>
<AddressEntity
address={{ hash: token.address }}
isLoading={ isLoading }
noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
</Flex>
</Td>
......
......@@ -5,10 +5,9 @@ import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntityWithAddressFilter from 'ui/shared/entities/token/TokenEntityWithAddressFilter';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean};
......@@ -17,17 +16,23 @@ const ERC721TokensListItem = ({ token, value, isLoading }: Props) => {
const hash = router.query.hash?.toString() || '';
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
return (
<ListItemMobile rowGap={ 2 }>
<Flex alignItems="center" width="100%">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontWeight="700" hash={ hash } tokenHash={ token.address } type="address_token" alias={ tokenString } isLoading={ isLoading }/>
</Flex>
<TokenEntityWithAddressFilter
token={ token }
isLoading={ isLoading }
addressHash={ hash }
noCopy
jointSymbol
fontWeight={ 700 }
/>
<Flex alignItems="center" pl={ 8 }>
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/>
<CopyToClipboard text={ token.address } isLoading={ isLoading }/>
<AddressEntity
address={{ hash: token.address }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Flex>
<HStack spacing={ 3 }>
......
......@@ -5,9 +5,8 @@ import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import TokenLogo from 'ui/shared/TokenLogo';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntityWithAddressFilter from 'ui/shared/entities/token/TokenEntityWithAddressFilter';
type Props = AddressTokenBalance & { isLoading: boolean};
......@@ -19,22 +18,26 @@ const ERC721TokensTableItem = ({
const router = useRouter();
const hash = router.query.hash?.toString() || '';
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
return (
<Tr>
<Td verticalAlign="middle">
<Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontWeight="700" hash={ hash } tokenHash={ token.address } type="address_token" alias={ tokenString } isLoading={ isLoading }/>
</Flex>
<TokenEntityWithAddressFilter
token={ token }
addressHash={ hash }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
/>
</Td>
<Td verticalAlign="middle">
<Flex alignItems="center" width="150px" justifyContent="space-between">
<Flex alignItems="center">
<AddressLink hash={ token.address } type="address" truncation="dynamic" isLoading={ isLoading }/>
<CopyToClipboard text={ token.address } isLoading={ isLoading }/>
</Flex>
<AddressEntity
address={{ hash: token.address }}
isLoading={ isLoading }
noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
</Flex>
</Td>
......
......@@ -5,10 +5,9 @@ import type { AddressTokenBalance } from 'types/api/address';
import { route } from 'nextjs-routes';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import NftMedia from 'ui/shared/nft/NftMedia';
import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TruncatedValue from 'ui/shared/TruncatedValue';
type Props = AddressTokenBalance & { isLoading: boolean };
......@@ -53,12 +52,12 @@ const NFTItem = ({ token, token_id: tokenId, token_instance: tokenInstance, isLo
</TruncatedTextTooltip>
</Flex>
) }
{ token.name && (
<Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } ml={ 1 } mr={ 1 } isLoading={ isLoading }/>
<TruncatedValue isLoading={ isLoading } value={ token.name } color="text_secondary"/>
</Flex>
) }
<TokenEntity
token={ token }
isLoading={ isLoading }
noCopy
noSymbol
/>
</LinkBox>
);
};
......
......@@ -5,11 +5,8 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
type Props = {
......@@ -31,19 +28,12 @@ const AddressesListItem = ({
return (
<ListItemMobile rowGap={ 3 }>
<Flex alignItems="center" justifyContent="space-between" w="100%">
<Address maxW="100%" mr={ 8 }>
<AddressIcon address={ item } mr={ 2 } isLoading={ isLoading }/>
<AddressLink
fontWeight={ 700 }
flexGrow={ 1 }
w="calc(100% - 32px)"
hash={ item.hash }
alias={ item.name }
type="address"
<AddressEntity
address={ item }
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">
<span>{ index }</span>
</Skeleton>
......
......@@ -5,11 +5,8 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
type Props = {
item: AddressesItem;
......@@ -37,20 +34,12 @@ const AddressesTableItem = ({
{ index }
</Skeleton>
</Td>
<Td>
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ item } mr={ 2 } isLoading={ isLoading }/>
<AddressLink
fontWeight={ 700 }
flexGrow={ 1 }
w="calc(100% - 32px)"
hash={ item.hash }
alias={ item.name }
type="address"
<Td verticalAlign="middle">
<AddressEntity
address={ item }
isLoading={ isLoading }
fontWeight={ 700 }
/>
<CopyToClipboard text={ item.hash } isLoading={ isLoading }/>
</Address>
</Td>
<Td pl={ 10 }>
{ item.public_tags && item.public_tags.length ? item.public_tags.map(tag => (
......
......@@ -20,11 +20,11 @@ import dayjs from 'lib/date/dayjs';
import { space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -206,8 +206,10 @@ const BlockDetails = ({ query }: Props) => {
columnGap={ 1 }
isLoading={ isPlaceholderData }
>
<AddressLink type="address" hash={ data.miner.hash } isLoading={ isPlaceholderData }/>
{ data.miner.name && <Text>{ `(${ capitalize(validatorTitle) }: ${ data.miner.name })` }</Text> }
<AddressEntity
address={ data.miner }
isLoading={ isPlaceholderData }
/>
{ /* api doesn't return the block processing time yet */ }
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
</DetailsInfoItem>
......
......@@ -13,8 +13,8 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -55,9 +55,12 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
<span>{ data.size.toLocaleString() } bytes</span>
</Skeleton>
</Flex>
<Flex columnGap={ 2 }>
<Flex columnGap={ 2 } w="100%">
<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 columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text>
......
......@@ -25,9 +25,9 @@ const BlocksTable = ({ data, isLoading, top, page }: Props) => {
<Tr>
<Th width="125px">Block</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={ 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 &&
<Th width="22%">Reward { config.chain.currency.symbol }</Th> }
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees &&
......
......@@ -12,8 +12,8 @@ import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -65,13 +65,8 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</Skeleton>
</Td>
<Td fontSize="sm">
<AddressLink
type="address"
alias={ data.miner.name }
hash={ data.miner.hash }
truncation="constant"
display="inline-flex"
maxW="100%"
<AddressEntity
address={ data.miner }
isLoading={ isLoading }
/>
</Td>
......
import { Box, Skeleton } from '@chakra-ui/react';
import React, { useCallback } from 'react';
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 TableItemActionButtons from 'ui/shared/TableItemActionButtons';
......@@ -25,7 +26,16 @@ const CustomAbiListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop
return (
<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 }/>
</ListItemMobile>
);
......
import {
Tr,
Td,
Box,
Skeleton,
} from '@chakra-ui/react';
import React, { useCallback } from 'react';
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';
interface Props {
......@@ -29,7 +31,16 @@ const CustomAbiTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro
return (
<Tr alignItems="top" key={ item.id }>
<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>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
......
......@@ -13,7 +13,7 @@ import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
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';
type Props = {
......@@ -66,7 +66,12 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => {
<Skeleton isLoaded={ !isLoading }>Reward</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.toFixed() }</span></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>
......
......@@ -14,10 +14,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
......@@ -83,31 +81,20 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
isLoading={ isLoading }
/>
<Box overflow="hidden" ml={ 1 }>
<Address mb={ 2 }>
<AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
fontSize="sm"
<AddressEntity
isLoading={ isLoading }
address={ tx.from }
fontSize="sm"
fontWeight="500"
mb={ 2 }
/>
</Address>
{ dataTo && (
<Address>
<AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
fontSize="sm"
<AddressEntity
isLoading={ isLoading }
address={ dataTo }
fontSize="sm"
fontWeight="500"
/>
</Address>
) }
</Box>
</Grid>
......
......@@ -13,10 +13,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
......@@ -67,19 +65,14 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
) }
</Flex>
<Flex alignItems="center" mb={ 3 }>
<Address mr={ 2 }>
<AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
<AddressEntity
isLoading={ isLoading }
address={ tx.from }
truncation="constant"
fontSize="sm"
isLoading={ isLoading }
fontWeight="500"
mr={ 2 }
/>
</Address>
<Icon
as={ rightArrowIcon }
boxSize={ 6 }
......@@ -87,19 +80,13 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
isLoading={ isLoading }
/>
{ dataTo && (
<Address ml={ 2 }>
<AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
<AddressEntity
isLoading={ isLoading }
address={ dataTo }
truncation="constant"
fontSize="sm"
isLoading={ isLoading }
fontWeight="500"
/>
</Address>
) }
</Flex>
<Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content">
......
......@@ -7,7 +7,7 @@ import config from 'configs/app';
import globeIcon from 'icons/globe.svg';
import txIcon from 'icons/transactions.svg';
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'> = {
id: 'daily_txs',
......@@ -30,13 +30,15 @@ const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = {
const nativeTokenData = {
name: config.chain.currency.name || '',
icon_url: '',
symbol: '',
address: '',
};
const coinPriceIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'coin_price',
title: `${ config.chain.currency.symbol } price`,
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.`,
api: {
resourceName: 'homepage_chart_market',
......
......@@ -4,16 +4,12 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app';
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 TxEntity from 'ui/shared/entities/tx/TxEntity';
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';
const feature = config.features.rollup;
......@@ -31,7 +27,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<ListItemMobileGrid.Value>
<BlockEntityL1
number={ item.l1_block_number }
isLoading={ isLoading }
......@@ -42,7 +38,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<ListItemMobileGrid.Value>
<TxEntity
isLoading={ isLoading }
hash={ item.l2_tx_hash }
......@@ -57,7 +53,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<ListItemMobileGrid.Value>
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_tx_hash }
......@@ -67,19 +63,12 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<LinkExternal
href={ feature.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) }
maxW="100%"
display="flex"
overflow="hidden"
<ListItemMobileGrid.Value>
<AddressEntityL1
address={{ hash: item.l1_tx_origin, name: '', is_contract: false, is_verified: false, implementation_name: '' }}
isLoading={ isLoading }
>
<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>
noCopy
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Gas limit</ListItemMobileGrid.Label>
......
......@@ -4,16 +4,12 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app';
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 TxEntity from 'ui/shared/entities/tx/TxEntity';
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;
......@@ -59,18 +55,12 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
/>
</Td>
<Td verticalAlign="middle">
<LinkExternal
href={ feature.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) }
maxW="100%"
display="inline-flex"
overflow="hidden"
<AddressEntityL1
address={{ hash: item.l1_tx_origin, name: '', is_contract: false, is_verified: false, implementation_name: '' }}
isLoading={ isLoading }
>
<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 }>
<HashStringShorten hash={ item.l1_tx_origin }/>
</Skeleton>
</LinkExternal>
truncation="constant"
noCopy
/>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block">
......
......@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import config from 'configs/app';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal';
......@@ -38,17 +36,17 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
{ item.from && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>From</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<Address>
<AddressIcon address={ item.from } isLoading={ isLoading }/>
<AddressLink hash={ item.from.hash } type="address" truncation="dynamic" ml={ 2 } isLoading={ isLoading }/>
</Address>
<ListItemMobileGrid.Value>
<AddressEntity
address={ item.from }
isLoading={ isLoading }
/>
</ListItemMobileGrid.Value>
</>
) }
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<ListItemMobileGrid.Value>
<TxEntity
isLoading={ isLoading }
hash={ item.l2_tx_hash }
......@@ -78,7 +76,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
{ item.l1_tx_hash && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<ListItemMobileGrid.Value>
<TxEntityL1
isLoading={ isLoading }
hash={ item.l1_tx_hash }
......
......@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import config from 'configs/app';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal';
......@@ -31,10 +29,11 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
</Td>
<Td verticalAlign="middle">
{ item.from ? (
<Address>
<AddressIcon address={ item.from } isLoading={ isLoading }/>
<AddressLink hash={ item.from.hash } type="address" truncation="constant" ml={ 2 } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ item.from }
isLoading={ isLoading }
truncation="constant"
/>
) : 'N/A' }
</Td>
<Td verticalAlign="middle">
......
import { Text } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -9,12 +8,9 @@ import { useAppContext } from 'lib/contexts/app';
import getQueryParamString from 'lib/router/getQueryParamString';
import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
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';
const ContractVerification = () => {
......@@ -100,15 +96,14 @@ const ContractVerification = () => {
title="New smart contract verification"
backLink={ backLink }
/>
{ hash && (
<Address mb={ 12 }>
<AddressIcon address={{ hash, is_contract: true, implementation_name: null }} flexShrink={ 0 }/>
<Text fontFamily="heading" ml={ 2 } fontWeight={ 500 } fontSize="lg" w={{ base: '100%', lg: 'auto' }} whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ hash }/>
</Text>
<CopyToClipboard text={ hash }/>
</Address>
) }
<AddressEntity
address={{ hash, is_contract: true, implementation_name: null }}
noLink
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
mb={ 12 }
/>
{ content }
</>
);
......
......@@ -11,11 +11,9 @@ import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities';
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 DataFetchAlert from 'ui/shared/DataFetchAlert';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle';
interface ExportTypeEntity {
......@@ -60,8 +58,8 @@ const isCorrectExportType = (type: string): type is CsvExportParams['type'] => O
const CsvExport = () => {
const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext();
const isMobile = useIsMobile();
const addressHash = router.query.address?.toString() || '';
const exportType = router.query.type?.toString() || '';
......@@ -133,10 +131,11 @@ const CsvExport = () => {
/>
<Flex mb={ 10 } whiteSpace="pre-wrap" flexWrap="wrap">
<span>Export { EXPORT_TYPES[exportType].text } for address </span>
<Address>
<AddressIcon address={{ hash: addressHash, is_contract: true, implementation_name: null }}/>
<AddressLink hash={ addressHash } type="address" ml={ 2 } truncation={ isMobile ? 'constant' : 'dynamic' }/>
</Address>
<AddressEntity
address={{ hash: addressHash, is_contract: true, implementation_name: null }}
truncation={ isMobile ? 'constant' : 'dynamic' }
noCopy
/>
<span>{ nbsp }</span>
{ filterType && filterValue && <span>with applied filter by { filterType } ({ filterValue }) </span> }
<span>to CSV file. </span>
......
......@@ -3,16 +3,12 @@ import { useRouter } from 'next/router';
import React from 'react';
import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle';
import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram';
const Sol2Uml = () => {
const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext();
const addressHash = router.query.address?.toString() || '';
......@@ -36,12 +32,11 @@ const Sol2Uml = () => {
title="Solidity UML diagram"
backLink={ backLink }
/>
<Flex mb={ 10 }>
<Flex mb={ 10 } flexWrap="wrap" columnGap={ 3 }>
<span>For contract</span>
<Address ml={ 3 }>
<AddressIcon address={{ hash: addressHash, is_contract: true, implementation_name: null }}/>
<AddressLink hash={ addressHash } type="address" ml={ 2 } truncation={ isMobile ? 'constant' : 'dynamic' }/>
</Address>
<AddressEntity
address={{ hash: addressHash, is_contract: true, implementation_name: null }}
/>
</Flex>
<Sol2UmlDiagram addressHash={ addressHash }/>
</>
......
......@@ -24,6 +24,7 @@ import * as tokenStubs from 'stubs/token';
import { generateListStub } from 'stubs/utils';
import AddressContract from 'ui/address/AddressContract';
import TextAd from 'ui/shared/ad/TextAd';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -31,7 +32,6 @@ import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TokenLogo from 'ui/shared/TokenLogo';
import TokenContractInfo from 'ui/token/TokenContractInfo';
import TokenDetails from 'ui/token/TokenDetails';
import TokenHolders from 'ui/token/TokenHolders/TokenHolders';
......@@ -270,14 +270,13 @@ const TokenPageContent = () => {
title={ `${ tokenQuery.data?.name || 'Unnamed token' }${ tokenSymbolText }` }
isLoading={ tokenQuery.isPlaceholderData }
backLink={ backLink }
beforeTitle={ (
<TokenLogo
data={ tokenQuery.data }
boxSize={ 6 }
beforeTitle={ tokenQuery.data ? (
<TokenEntity.Icon
token={ tokenQuery.data }
isLoading={ tokenQuery.isPlaceholderData }
mr={ 2 }
iconSize="lg"
/>
) }
) : null }
contentAfter={ titleContentAfter }
/>
<TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/>
......
......@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
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 TableItemActionButtons from 'ui/shared/TableItemActionButtons';
......@@ -26,7 +26,12 @@ const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pro
return (
<ListItemMobile>
<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 }>
<Text fontSize="sm" fontWeight={ 500 }>Private tag</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm">
......
......@@ -6,8 +6,8 @@ import React, { useCallback } from 'react';
import type { AddressTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props {
......@@ -29,7 +29,12 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pr
return (
<Tr alignItems="top" key={ item.id }>
<Td>
<AddressSnippet address={ item.address } isLoading={ isLoading }/>
<AddressEntity
address={ item.address }
isLoading={ isLoading }
fontWeight="600"
py="2px"
/>
</Td>
<Td whiteSpace="nowrap">
<Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag>
......
......@@ -3,8 +3,8 @@ import React, { useCallback } from 'react';
import type { PublicTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
......@@ -28,7 +28,15 @@ const PublicTagListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop
<ListItemMobile>
<VStack spacing={ 3 } alignItems="flex-start" 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>
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Public tags</Text>
......
......@@ -9,8 +9,8 @@ import React, { useCallback } from 'react';
import type { PublicTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props {
......@@ -33,7 +33,15 @@ const PublicTagTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro
<Tr alignItems="top" key={ item.id }>
<Td>
<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>
</Td>
<Td>
......
......@@ -6,17 +6,16 @@ import type { SearchResultItem } from 'types/api/search';
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 verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
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 HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal';
......@@ -24,7 +23,6 @@ import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
data: SearchResultItem | SearchResultAppItem;
......@@ -52,9 +50,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
return (
<Flex alignItems="center" overflow="hidden">
<TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/>
<TokenEntity.Icon token={ data } isLoading={ isLoading }/>
<LinkInternal
ml={ 2 }
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
wordBreak="break-all"
......@@ -79,14 +76,31 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
case 'contract':
case 'address': {
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 (
<Address>
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/>
<Box as={ shouldHighlightHash ? 'mark' : 'span' } display="block" whiteSpace="nowrap" overflow="hidden">
<AddressLink type="address" hash={ data.address } fontWeight={ 700 } display="block" w="100%" onClick={ handleLinkClick }/>
</Box>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
</Address>
<AddressEntity.Container>
<AddressEntity.Icon address={ address }/>
<AddressEntity.Link
address={ address }
onClick={ handleLinkClick }
>
<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';
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 verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
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 HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
data: SearchResultItem | SearchResultAppItem;
......@@ -53,9 +51,8 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<>
<Td fontSize="sm">
<Flex alignItems="center">
<TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/>
<TokenEntity.Icon token={ data } isLoading={ isLoading }/>
<LinkInternal
ml={ 2 }
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
wordBreak="break-all"
......@@ -98,25 +95,33 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
case 'address': {
if (data.name) {
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 (
<>
<Td fontSize="sm">
<Flex alignItems="center" overflow="hidden">
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/>
<LinkInternal
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
overflow="hidden"
whiteSpace="nowrap"
<AddressEntity.Container>
<AddressEntity.Icon address={ address }/>
<AddressEntity.Link
address={ address }
onClick={ handleLinkClick }
>
<Box as={ shouldHighlightHash ? 'mark' : 'span' } display="block">
<HashStringShortenDynamic hash={ data.address }/>
</Box>
</LinkInternal>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
</Flex>
<AddressEntity.Content
asProp={ shouldHighlightHash ? 'mark' : 'span' }
address={ address }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td>
<Td colSpan={ 2 } fontSize="sm" verticalAlign="middle">
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/>
......@@ -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 (
<Td colSpan={ 3 } fontSize="sm">
<Address>
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/>
<mark>
<AddressLink hash={ data.address } type="address" fontWeight={ 700 } onClick={ handleLinkClick }/>
</mark>
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> }
</Address>
<AddressEntity.Container>
<AddressEntity.Icon address={ address }/>
<AddressEntity.Link
address={ address }
onClick={ handleLinkClick }
>
<AddressEntity.Content
asProp="mark"
address={ address }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td>
);
}
......
......@@ -8,10 +8,8 @@ import config from 'configs/app';
import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressQrCode from 'ui/address/details/AddressQrCode';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
address: Pick<Address, 'hash' | 'is_contract' | 'implementation_name' | 'watchlist_names' | 'watchlist_address_id'>;
......@@ -23,18 +21,14 @@ interface Props {
const AddressHeadingInfo = ({ address, token, isLinkDisabled, isLoading }: Props) => {
return (
<Flex alignItems="center">
<AddressIcon address={ address } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ address.hash }
ml={ 2 }
<AddressEntity
address={{ ...address, name: '' }}
isLoading={ isLoading }
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
isDisabled={ isLinkDisabled }
isLoading={ isLoading }
noLink={ isLinkDisabled }
/>
<CopyToClipboard text={ address.hash } isLoading={ isLoading }/>
{ !isLoading && address.is_contract && token && <AddressAddToWallet ml={ 2 } token={ token }/> }
{ !isLoading && !address.is_contract && config.features.account.isEnabled && (
<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) => {
}, [ hasCopied ]);
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 (
......@@ -42,7 +42,8 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => {
className={ className }
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
ml={ 1 }
ml={ 2 }
borderRadius={ 0 }
/>
</Tooltip>
);
......
......@@ -5,9 +5,9 @@ import type { TokenInfo } from 'types/api/token';
import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle';
......@@ -49,7 +49,10 @@ const DefaultView = () => {
<PageTitle
title="Shavukha Token (SHVKH) token"
beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/>
<TokenEntity.Icon
token={ tokenData }
iconSize="lg"
/>
) }
backLink={ backLink }
contentAfter={ contentAfter }
......
......@@ -7,9 +7,9 @@ import type { TokenInfo } from 'types/api/token';
import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import { publicTag, privateTag, watchlistName } from 'mocks/address/tag';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle';
......@@ -57,7 +57,10 @@ const LongNameAndManyTags = () => {
<PageTitle
title={ `${ tokenData?.name }${ tokenSymbolText } token` }
beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/>
<TokenEntity.Icon
token={ tokenData }
iconSize="lg"
/>
) }
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';
import eastArrowIcon from 'icons/arrows/east.svg';
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 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 InOutTag from 'ui/shared/InOutTag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
......@@ -55,7 +52,13 @@ const TokenTransferListItem = ({
<ListItemMobile rowGap={ 3 } isAnimated>
<Flex w="100%" justifyContent="space-between">
<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 colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex>
......@@ -80,11 +83,14 @@ const TokenTransferListItem = ({
</Flex>
) }
<Flex w="100%" columnGap={ 3 }>
<Address width={ addressWidth } flexShrink={ 0 }>
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ baseAddress === from.hash } isLoading={ isLoading }/>
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address>
<AddressEntity
address={ from }
isLoading={ isLoading }
noLink={ baseAddress === from.hash }
noCopy={ baseAddress === from.hash }
flexShrink={ 0 }
width={ addressWidth }
/>
{ baseAddress ? (
<InOutTag
isIn={ baseAddress === to.hash }
......@@ -97,11 +103,14 @@ const TokenTransferListItem = ({
) :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading } flexShrink={ 0 }/>
}
<Address width={ addressWidth } flexShrink={ 0 }>
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ to.hash } isDisabled={ baseAddress === to.hash } isLoading={ isLoading }/>
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash } isLoading={ isLoading }/> }
</Address>
<AddressEntity
address={ to }
isLoading={ isLoading }
noLink={ baseAddress === to.hash }
noCopy={ baseAddress === to.hash }
flexShrink={ 0 }
width={ addressWidth }
/>
</Flex>
{ value && (
<Flex columnGap={ 2 } w="100%">
......
......@@ -5,14 +5,11 @@ import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
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 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 InOutTag from 'ui/shared/InOutTag';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
......@@ -50,7 +47,13 @@ const TokenTransferTableItem = ({
) }
<Td>
<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 colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex>
......@@ -75,19 +78,14 @@ const TokenTransferTableItem = ({
</Td>
) }
<Td>
<Address display="inline-flex" maxW="100%" my="3px">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink
type="address" ml={ 2 }
fontWeight="500"
hash={ from.hash }
alias={ from.name }
flexGrow={ 1 }
isDisabled={ baseAddress === from.hash }
<AddressEntity
address={ from }
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>
{ baseAddress && (
<Td px={ 0 }>
......@@ -103,20 +101,14 @@ const TokenTransferTableItem = ({
</Td>
) }
<Td>
<Address display="inline-flex" maxW="100%" my="3px">
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink
type="address"
ml={ 2 }
fontWeight="500"
hash={ to.hash }
alias={ to.name }
flexGrow={ 1 }
isDisabled={ baseAddress === to.hash }
<AddressEntity
address={ to }
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 isNumeric verticalAlign="top">
<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) => {
);
});
type IconProps = Pick<EntityProps, 'address' | 'isLoading' | 'iconSize'> & {
type IconProps = Pick<EntityProps, 'address' | 'isLoading' | 'iconSize' | 'noIcon'> & {
asProp?: As;
};
const Icon = (props: IconProps) => {
if (props.noIcon) {
return null;
}
const styles = {
...getIconProps(props.iconSize),
marginRight: 2,
......@@ -46,19 +50,29 @@ const Icon = (props: IconProps) => {
if (props.address.is_contract) {
if (props.address.is_verified) {
return (
<Tooltip label="Verified contract">
<span>
<EntityBase.Icon
{ ...props }
asProp={ iconContractVerified }
color="green.500"
borderRadius={ 0 }
/>
</span>
</Tooltip>
);
}
return (
<Tooltip label="Contract">
<span>
<EntityBase.Icon
{ ...props }
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 _omit from 'lodash/omit';
import React from 'react';
import { route } from 'nextjs-routes';
......@@ -11,8 +10,6 @@ interface Props extends AddressEntity.EntityProps {
}
const AddressEntityWithTokenFilter = (props: Props) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
const defaultHref = route({
pathname: '/address/[hash]',
query: {
......@@ -25,15 +22,7 @@ const AddressEntityWithTokenFilter = (props: Props) => {
});
return (
<AddressEntity.Container className={ props.className }>
<AddressEntity.Icon { ...partsProps }/>
<AddressEntity.Link
{ ...linkProps }
href={ props.href ?? defaultHref }
>
<AddressEntity.Content { ...partsProps }/>
</AddressEntity.Link>
</AddressEntity.Container>
<AddressEntity.default { ...props } href={ props.href ?? defaultHref }/>
);
};
......
......@@ -26,6 +26,7 @@ export interface EntityBaseProps {
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void;
query?: Record<string, string>;
tailLength?: number;
target?: React.HTMLAttributeAnchorTarget;
truncation?: Truncation;
}
......@@ -77,9 +78,10 @@ const Link = chakra(({ isLoading, children, isExternal, onClick, href, noLink }:
export interface IconBaseProps extends Pick<EntityBaseProps, 'isLoading' | 'iconSize' | 'noIcon'> {
asProp: As;
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');
if (noIcon) {
......@@ -93,7 +95,7 @@ const Icon = ({ isLoading, iconSize, noIcon, asProp, color }: IconBaseProps) =>
as={ asProp }
boxSize={ styles.boxSize }
isLoading={ isLoading }
borderRadius="base"
borderRadius={ borderRadius ?? 'base' }
/>
</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 _omit from 'lodash/omit';
import React from 'react';
......@@ -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;
marginRight?: ChakraProps['marginRight'];
boxSize?: ChakraProps['boxSize'];
};
const Icon = (props: IconProps) => {
......@@ -38,18 +40,19 @@ const Icon = (props: IconProps) => {
}
const styles = {
...getIconProps(props.iconSize),
marginRight: 2,
marginRight: props.marginRight ?? 2,
boxSize: props.boxSize ?? getIconProps(props.iconSize).boxSize,
borderRadius: 'base',
};
if (props.isLoading) {
return <Skeleton { ...styles } flexShrink={ 0 }/>;
return <Skeleton { ...styles } className={ props.className } flexShrink={ 0 }/>;
}
return (
<Image
{ ...styles }
className={ props.className }
src={ props.token.icon_url ?? undefined }
alt={ `${ props.token.name || 'token' } logo` }
fallback={ <TokenLogoPlaceholder { ...styles }/> }
......@@ -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 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 (
<TruncatedTextTooltip label={ name }>
<TruncatedTextTooltip label={ nameString }>
<Skeleton
isLoaded={ !props.isLoading }
display="inline-block"
......@@ -72,18 +79,18 @@ const Content = chakra((props: ContentProps) => {
textOverflow="ellipsis"
height="fit-content"
>
{ name }
{ nameString }
</Skeleton>
</TruncatedTextTooltip>
);
});
type SymbolProps = Pick<EntityProps, 'token' | 'isLoading' | 'noSymbol'>;
type SymbolProps = Pick<EntityProps, 'token' | 'isLoading' | 'noSymbol' | 'jointSymbol' | 'onlySymbol'>;
const Symbol = (props: SymbolProps) => {
const symbol = props.token.symbol;
if (!symbol || props.noSymbol) {
if (!symbol || props.noSymbol || props.jointSymbol || props.onlySymbol) {
return null;
}
......@@ -129,6 +136,8 @@ const Container = EntityBase.Container;
export interface EntityProps extends EntityBase.EntityBaseProps {
token: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>;
noSymbol?: boolean;
jointSymbol?: boolean;
onlySymbol?: boolean;
}
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';
import type { DecodedInput } from 'types/api/decodedInput';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TruncatedValue from 'ui/shared/TruncatedValue';
interface Props {
......@@ -33,10 +32,10 @@ const Row = ({ name, type, indexed, value, isLoading }: ArrayElement<DecodedInpu
const content = (() => {
if (type === 'address' && typeof value === 'string') {
return (
<Address justifyContent="space-between">
<AddressLink type="address" hash={ value } isLoading={ isLoading }/>
<CopyToClipboard text={ value } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={{ hash: value, name: '', implementation_name: null, is_contract: false, is_verified: false }}
isLoading={ isLoading }
/>
);
}
......
......@@ -7,9 +7,8 @@ import { route } from 'nextjs-routes';
// import searchIcon from 'icons/search.svg';
import { space } from 'lib/html-entities';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import LogTopic from 'ui/shared/logs/LogTopic';
......@@ -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> }
<GridItem display="flex" alignItems="center">
<Address mr={{ base: 9, lg: 0 }}>
{ !hasTxInfo && <AddressIcon address={ address } mr={ 2 } isLoading={ isLoading }/> }
<AddressLink
hash={ hasTxInfo ? txHash : address.hash }
alias={ hasTxInfo ? undefined : address.name }
type={ type === 'address' ? 'transaction' : 'address' }
{ type === 'address' ? (
<TxEntity
hash={ txHash }
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 */ }
{ /* <Tooltip label="Find matches topic">
<Link ml={ 2 } mr={{ base: 9, lg: 0 }} display="inline-flex">
......
......@@ -4,9 +4,8 @@ import React from 'react';
import hexToAddress from 'lib/hexToAddress';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
......@@ -51,10 +50,10 @@ const LogTopic = ({ hex, index, isLoading }: Props) => {
case 'address': {
return (
<Address>
<AddressLink type="address" hash={ value } isLoading={ isLoading }/>
<CopyToClipboard text={ value } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={{ hash: value, name: '', implementation_name: null, is_contract: false, is_verified: false }}
isLoading={ isLoading }
/>
);
}
}
......
import { Box, Text, Grid, Flex, Icon } from '@chakra-ui/react';
import { Box, Text, Flex } from '@chakra-ui/react';
import React from 'react';
import type { SearchResultAddressOrContract } from 'types/api/search';
import iconSuccess from 'icons/status/success.svg';
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';
interface Props {
......@@ -16,7 +15,11 @@ interface Props {
const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
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 && (
<Text
variant="secondary"
......@@ -27,15 +30,13 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
<span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/>
</Text>
);
const isContractVerified = data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/>;
const address = <HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>;
if (isMobile) {
return (
<>
<Grid templateColumns="24px 1fr" gap={ 2 }>
<Flex alignItems="center">
{ icon }
<Flex alignItems="center" overflow="hidden">
<Box
as={ shouldHighlightHash ? 'mark' : 'span' }
display="block"
......@@ -45,18 +46,16 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
>
{ address }
</Box>
{ isContractVerified }
</Flex>
</Grid>
{ name }
</>
);
}
return (
<Flex alignItems="center" gap={ 2 }>
<Flex alignItems="center">
<Flex alignItems="center" w="450px" mr={ 2 }>
{ icon }
<Flex alignItems="center" w="450px" overflow="hidden">
<Box
as={ shouldHighlightHash ? 'mark' : 'span' }
display="block"
......@@ -66,7 +65,6 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
>
{ address }
</Box>
{ isContractVerified }
</Flex>
{ name }
</Flex>
......
......@@ -21,7 +21,7 @@ const SearchBarSuggestApp = ({ data, isMobile, searchTerm, onClick }: Props) =>
const logo = (
<Image
borderRadius="base"
boxSize={ 6 }
boxSize={ 5 }
src={ useColorModeValue(data.logo, data.logoDarkMode || data.logo) }
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 type { SearchResultBlock } from 'types/api/search';
import blockIcon from 'icons/block.svg';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
......@@ -15,7 +15,7 @@ interface 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 blockNumber = (
<Text
......@@ -43,7 +43,7 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => {
if (isMobile) {
return (
<>
<Flex alignItems="center" gap={ 2 }>
<Flex alignItems="center">
{ icon }
{ blockNumber }
</Flex>
......@@ -54,9 +54,11 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => {
}
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 }
{ blockNumber }
</Flex>
{ hash }
<Text variant="secondary" textAlign="end">{ date }</Text>
</Grid>
......
......@@ -3,7 +3,7 @@ import React from 'react';
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 highlightText from 'lib/highlightText';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
......@@ -15,7 +15,7 @@ interface 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 = (
<Text
......@@ -56,9 +56,11 @@ const SearchBarSuggestLabel = ({ data, isMobile, searchTerm }: Props) => {
}
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 }
{ name }
</Flex>
<Flex alignItems="center" overflow="hidden" gap={ 1 }>
{ address }
{ isContractVerified }
......
......@@ -6,8 +6,8 @@ import type { SearchResultToken } from 'types/api/search';
import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg';
import highlightText from 'lib/highlightText';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
data: SearchResultToken;
......@@ -16,8 +16,8 @@ interface Props {
}
const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
const icon = <TokenLogo boxSize={ 6 } data={ data }/>;
const verifiedIcon = <Icon as={ verifiedToken } boxSize={ 4 } color="green.500"/>;
const icon = <TokenEntity.Icon token={ data }/>;
const verifiedIcon = <Icon as={ verifiedToken } boxSize={ 4 } color="green.500" ml={ 1 }/>;
const name = (
<Text
fontWeight={ 700 }
......@@ -49,13 +49,11 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
return (
<>
<Flex alignItems="center" gap={ 2 }>
<Flex alignItems="center">
{ icon }
<Flex alignItems="center" gap={ 1 }>
{ name }
{ data.is_verified_via_admin_panel && verifiedIcon }
</Flex>
</Flex>
<Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }>
<Flex alignItems="center" overflow="hidden">
{ address }
......@@ -68,9 +66,9 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
}
return (
<Grid templateColumns="24px 200px 1fr auto" gap={ 2 }>
<Grid templateColumns="228px 1fr auto" gap={ 2 }>
<Flex alignItems="center">
{ icon }
<Flex alignItems="center" gap={ 1 }>
{ name }
{ data.is_verified_via_admin_panel && verifiedIcon }
</Flex>
......
import { chakra, Grid, Text, Flex, Icon } from '@chakra-ui/react';
import { chakra, Text, Flex } from '@chakra-ui/react';
import React from 'react';
import type { SearchResultTx } from 'types/api/search';
import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
......@@ -14,7 +14,7 @@ interface Props {
}
const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
const icon = <Icon as={ txIcon } boxSize={ 6 } color="gray.500"/>;
const icon = <TxEntity.Icon/>;
const hash = (
<chakra.mark overflow="hidden" whiteSpace="nowrap" fontWeight={ 700 }>
<HashStringShortenDynamic hash={ data.tx_hash } isTooltipDisabled/>
......@@ -25,7 +25,7 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
if (isMobile) {
return (
<>
<Flex alignItems="center" justifyContent="space-between" gap={ 2 }>
<Flex alignItems="center">
{ icon }
{ hash }
</Flex>
......@@ -35,11 +35,13 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
}
return (
<Grid templateColumns="24px minmax(auto, max-content) auto" gap={ 2 }>
<Flex columnGap={ 2 }>
<Flex alignItems="center" minW={ 0 }>
{ icon }
{ hash }
<Text variant="secondary" textAlign="end">{ date }</Text>
</Grid>
</Flex>
<Text variant="secondary" textAlign="end" flexShrink={ 0 } ml="auto">{ date }</Text>
</Flex>
);
};
......
......@@ -4,10 +4,7 @@ import React from 'react';
import type { TokenHolder, TokenInfo } 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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import Utilization from 'ui/shared/Utilization/Utilization';
......@@ -22,19 +19,12 @@ const TokenHoldersListItem = ({ holder, token, isLoading }: Props) => {
return (
<ListItemMobile rowGap={ 3 }>
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ holder.address } isLoading={ isLoading }/>
<AddressLink
type="address"
ml={ 2 }
fontWeight="700"
hash={ holder.address.hash }
alias={ holder.address.name }
flexGrow={ 1 }
<AddressEntity
address={ holder.address }
isLoading={ isLoading }
fontWeight="700"
maxW="100%"
/>
<CopyToClipboard text={ holder.address.hash } isLoading={ isLoading }/>
</Address>
<Flex justifyContent="space-between" alignItems="center" width="100%">
<Skeleton isLoaded={ !isLoading } display="inline-block" width="100%">
<Box as="span" wordBreak="break-word" mr={ 6 }>
......
......@@ -4,10 +4,7 @@ import React from 'react';
import type { TokenHolder, TokenInfo } 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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import Utilization from 'ui/shared/Utilization/Utilization';
type Props = {
......@@ -22,20 +19,12 @@ const TokenTransferTableItem = ({ holder, token, isLoading }: Props) => {
return (
<Tr>
<Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ holder.address } isLoading={ isLoading }/>
<AddressLink
type="address"
ml={ 2 }
fontWeight="700"
hash={ holder.address.hash }
alias={ holder.address.name }
flexGrow={ 1 }
<AddressEntity
address={ holder.address }
isLoading={ isLoading }
truncation="constant"
flexGrow={ 1 }
fontWeight="700"
/>
<CopyToClipboard text={ holder.address.hash } isLoading={ isLoading }/>
</Address>
</Td>
<Td verticalAlign="middle" isNumeric>
<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 React from 'react';
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 useIsMobile from 'lib/hooks/useIsMobile';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkInternal from 'ui/shared/LinkInternal';
import NftMedia from 'ui/shared/nft/NftMedia';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
......@@ -15,6 +14,8 @@ type Props = { item: TokenInstance; isLoading: boolean };
const NFTItem = ({ item, isLoading }: Props) => {
const isMobile = useIsMobile();
const mediaElement = (
<NftMedia
mb="18px"
......@@ -67,10 +68,13 @@ const NFTItem = ({ item, isLoading }: Props) => {
{ item.owner && (
<Flex mb={ 2 } ml={ 1 }>
<Text whiteSpace="pre" variant="secondary" mr={ 2 } lineHeight="24px">Owner</Text>
<Address>
<Hide below="lg" ssr={ false }><AddressIcon address={ item.owner } mr={ 1 } isLoading={ isLoading }/></Hide>
<AddressLink hash={ item.owner.hash } alias={ item.owner.name } type="address" truncation="constant" isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ item.owner }
isLoading={ isLoading }
truncation="constant"
noCopy
noIcon={ isMobile }
/>
</Flex>
) }
</LinkBox>
......
......@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg';
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 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 ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
......@@ -59,17 +56,21 @@ const TokenTransferListItem = ({
</Flex>
{ method && <Tag isLoading={ isLoading }>{ method }</Tag> }
<Flex w="100%" columnGap={ 3 }>
<Address width="50%">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/>
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/>
</Address>
<AddressEntityWithTokenFilter
address={ from }
isLoading={ isLoading }
tokenHash={ token.address }
width="50%"
fontWeight="500"
/>
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
<Address width="50%">
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/>
<CopyToClipboard text={ to.hash } isLoading={ isLoading }/>
</Address>
<AddressEntityWithTokenFilter
address={ to }
isLoading={ isLoading }
tokenHash={ token.address }
width="50%"
fontWeight="500"
/>
</Flex>
{ value && (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
<Flex columnGap={ 2 } w="100%">
......
......@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg';
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 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 TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
......@@ -57,21 +54,13 @@ const TokenTransferTableItem = ({
) : null }
</Td>
<Td>
<Address display="inline-flex" maxW="100%" py="3px">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink
ml={ 2 }
flexGrow={ 1 }
fontWeight="500"
type="address_token"
hash={ from.hash }
alias={ from.name }
tokenHash={ token.address }
truncation="constant"
<AddressEntityWithTokenFilter
address={ from }
isLoading={ isLoading }
truncation="constant"
tokenHash={ token.address }
my="5px"
/>
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/>
</Address>
</Td>
<Td px={ 0 }>
<Box my="3px">
......@@ -79,21 +68,13 @@ const TokenTransferTableItem = ({
</Box>
</Td>
<Td>
<Address display="inline-flex" maxW="100%" py="3px">
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink
ml={ 2 }
flexGrow={ 1 }
fontWeight="500"
type="address_token"
hash={ to.hash }
alias={ to.name }
tokenHash={ token.address }
truncation="constant"
<AddressEntityWithTokenFilter
address={ to }
isLoading={ isLoading }
truncation="constant"
tokenHash={ token.address }
my="5px"
/>
<CopyToClipboard text={ to.hash } isLoading={ isLoading }/>
</Address>
</Td>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
<Td>
......
......@@ -3,15 +3,13 @@ import React from 'react';
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 DetailsInfoItem from 'ui/shared/DetailsInfoItem';
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 NftMedia from 'ui/shared/nft/NftMedia';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress';
import TokenInstanceDivider from './details/TokenInstanceDivider';
......@@ -51,7 +49,11 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => {
hint="Token name"
isLoading={ isLoading }
>
<TokenSnippet data={ data.token } isLoading={ isLoading }/>
<TokenEntity
token={ data.token }
isLoading={ isLoading }
noCopy
/>
</DetailsInfoItem>
{ data.is_unique && data.owner && (
<DetailsInfoItem
......@@ -59,11 +61,10 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => {
hint="Current owner of this token instance"
isLoading={ isLoading }
>
<Address>
<AddressIcon address={ data.owner } isLoading={ isLoading }/>
<AddressLink type="address" hash={ data.owner.hash } ml={ 2 } isLoading={ isLoading }/>
<CopyToClipboard text={ data.owner.hash } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ data.owner }
isLoading={ isLoading }
/>
</DetailsInfoItem>
) }
<TokenInstanceCreatorAddress hash={ isLoading ? '' : data.token.address }/>
......
......@@ -2,11 +2,8 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props {
hash: string;
......@@ -41,11 +38,10 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
hint="Address that deployed this token contract"
isLoading={ addressQuery.isPlaceholderData }
>
<Address>
<AddressIcon address={ creatorAddress } isLoading={ addressQuery.isPlaceholderData }/>
<AddressLink type="address" hash={ creatorAddress.hash } ml={ 2 } isLoading={ addressQuery.isPlaceholderData }/>
<CopyToClipboard text={ creatorAddress.hash } isLoading={ addressQuery.isPlaceholderData }/>
</Address>
<AddressEntity
address={ creatorAddress }
isLoading={ addressQuery.isPlaceholderData }
/>
</DetailsInfoItem>
);
};
......
......@@ -5,11 +5,10 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
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 TokenLogo from 'ui/shared/TokenLogo';
type Props = {
token: TokenInfo;
......@@ -31,14 +30,10 @@ const TokensTableItem = ({
address,
exchange_rate: exchangeRate,
type,
name,
symbol,
holders,
circulating_market_cap: marketCap,
} = token;
const tokenString = [ name, symbol && `(${ symbol })` ].filter(Boolean).join(' ');
return (
<ListItemMobile rowGap={ 3 }>
<Grid
......@@ -46,25 +41,30 @@ const TokensTableItem = ({
gridTemplateColumns="minmax(0, 1fr)"
>
<GridItem display="flex">
<Flex overflow="hidden" mr={ 3 } alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading } mr={ 3 }/>
<Tag flexShrink={ 0 } isLoading={ isLoading }>{ type }</Tag>
</Flex>
<TokenEntity
token={ token }
isLoading={ isLoading }
jointSymbol
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 }>
<span>{ (page - 1) * PAGE_SIZE + index + 1 }</span>
</Skeleton>
</GridItem>
</Grid>
<Flex justifyContent="space-between" alignItems="center" width="100%">
<Flex alignItems="center" width="136px" justifyContent="space-between" ml={ 8 } mt="-8px">
<Flex alignItems="center">
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" isLoading={ isLoading }/>
<CopyToClipboard text={ address } isLoading={ isLoading }/>
</Flex>
<Flex justifyContent="space-between" alignItems="center" width="150px" ml={ 7 } mt={ -2 }>
<AddressEntity
address={{ hash: address }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } isLoading={ isLoading }/>
</Flex>
</Flex>
{ exchangeRate && (
<HStack spacing={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton>
......
......@@ -4,12 +4,11 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token';
import Address from 'ui/shared/address/Address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import TokenLogo from 'ui/shared/TokenLogo';
import type { EntityProps as AddressEntityProps } from 'ui/shared/entities/address/AddressEntity';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
type Props = {
token: TokenInfo;
......@@ -31,13 +30,17 @@ const TokensTableItem = ({
address,
exchange_rate: exchangeRate,
type,
name,
symbol,
holders,
circulating_market_cap: marketCap,
} = 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 (
<Tr>
......@@ -46,7 +49,7 @@ const TokensTableItem = ({
<Skeleton
isLoaded={ !isLoading }
fontSize="sm"
lineHeight="24px"
lineHeight="20px"
fontWeight={ 600 }
mr={ 3 }
minW="28px"
......@@ -54,16 +57,26 @@ const TokensTableItem = ({
{ (page - 1) * PAGE_SIZE + index + 1 }
</Skeleton>
<Box overflow="hidden">
<Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading }/>
</Flex>
<Box ml={ 8 } mt={ 2 }>
<Address>
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" fontWeight={ 500 } isLoading={ isLoading }/>
<CopyToClipboard text={ address } isLoading={ isLoading }/>
<TokenEntity
token={ token }
isLoading={ isLoading }
jointSymbol
noCopy
fontSize="sm"
fontWeight="700"
/>
<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 }/>
</Address>
</Flex>
<Box mt={ 3 } >
<Tag isLoading={ isLoading }>{ type }</Tag>
</Box>
......
......@@ -6,9 +6,8 @@ import type { TokenInfo } from 'types/api/token';
import { route } from 'nextjs-routes';
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 TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
interface Props {
token: TokenInfo;
......@@ -29,11 +28,10 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => {
{ tokenId.length > 8 ? <HashStringShorten hash={ tokenId }/> : tokenId }
</Link>
</Box>
{ token.name ? (
<TokenSnippet data={ token } logoSize={ 5 } columnGap={ 1 }/>
) : (
<AddressLink hash={ token.address } truncation="constant" type="token"/>
) }
<TokenEntity
token={ token }
noCopy
/>
</Flex>
);
};
......
......@@ -26,9 +26,6 @@ import { WEI, WEI_IN_GWEI } from 'lib/consts';
import dayjs from 'lib/date/dayjs';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
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 Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
......@@ -36,13 +33,12 @@ import CurrencyValue from 'ui/shared/CurrencyValue';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
// import PrevNext from 'ui/shared/PrevNext';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator';
import TruncatedValue from 'ui/shared/TruncatedValue';
import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization/Utilization';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions';
......@@ -141,8 +137,6 @@ const TxDetails = () => {
<HashStringShortenDynamic hash={ data.hash }/>
</Skeleton>
<CopyToClipboard text={ data.hash } isLoading={ isPlaceholderData }/>
{ /* api doesn't support navigation between certain address account tx */ }
{ /* <PrevNext ml={ 7 }/> */ }
</DetailsInfoItem>
<DetailsInfoItem
title="Status and method"
......@@ -219,11 +213,10 @@ const TxDetails = () => {
isLoading={ isPlaceholderData }
columnGap={ 3 }
>
<Address>
<AddressIcon address={ data.from } isLoading={ isPlaceholderData }/>
<AddressLink type="address" ml={ 2 } hash={ data.from.hash } isLoading={ isPlaceholderData }/>
<CopyToClipboard text={ data.from.hash } isLoading={ isPlaceholderData }/>
</Address>
<AddressEntity
address={ data.from }
isLoading={ isPlaceholderData }
/>
{ data.from.name && <Text>{ data.from.name }</Text> }
{ addressFromTags.length > 0 && (
<Flex columnGap={ 3 }>
......@@ -241,24 +234,27 @@ const TxDetails = () => {
{ toAddress ? (
<>
{ data.to && data.to.hash ? (
<Address alignItems="center" flexShrink={ 0 } w={{ base: '100%', lg: 'auto' }}>
<AddressIcon address={ toAddress } isLoading={ isPlaceholderData }/>
<AddressLink type="address" ml={ 2 } hash={ toAddress.hash } isLoading={ isPlaceholderData }/>
<Flex flexWrap="nowrap" alignItems="center" maxW="100%">
<AddressEntity
address={ toAddress }
isLoading={ isPlaceholderData }
/>
{ executionSuccessBadge }
{ executionFailedBadge }
<CopyToClipboard text={ toAddress.hash } isLoading={ isPlaceholderData }/>
</Address>
</Flex>
) : (
<Flex width={{ base: '100%', lg: 'auto' }} whiteSpace="pre" alignItems="center" flexShrink={ 0 }>
<Flex width="100%" whiteSpace="pre" alignItems="center" flexShrink={ 0 }>
<span>[Contract </span>
<AddressLink type="address" hash={ toAddress.hash }/>
<span> created]</span>
<AddressEntity
address={ toAddress }
isLoading={ isPlaceholderData }
noIcon
/>
<span>created]</span>
{ executionSuccessBadge }
{ executionFailedBadge }
<CopyToClipboard text={ toAddress.hash }/>
</Flex>
) }
{ toAddress.name && <TruncatedValue value={ toAddress.name }/> }
{ addressToTags.length > 0 && (
<Flex columnGap={ 3 }>
{ addressToTags }
......@@ -283,6 +279,7 @@ const TxDetails = () => {
currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate }
isLoading={ isPlaceholderData }
flexWrap="wrap"
/>
</DetailsInfoItem>
<DetailsInfoItem
......
......@@ -8,8 +8,8 @@ import { route } from 'nextjs-routes';
import config from 'configs/app';
import uniswapIcon from 'icons/uniswap.svg';
import AddressLink from 'ui/shared/address/AddressLink';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
interface Props {
action: TxAction;
......@@ -58,34 +58,32 @@ const TxDetailsAction = ({ action }: Props) => {
<Flex flexWrap="wrap" columnGap={ 1 } rowGap={ 2 } alignItems="center">
<chakra.span color="text_secondary">{ text0 }: </chakra.span>
<chakra.span fontWeight={ 600 }>{ amount0 }</chakra.span>
<TokenSnippet
data={ token0 }
<chakra.span fontWeight={ 600 } mr={ 1 }>{ amount0 }</chakra.span>
<TokenEntity
token={ token0 }
noLink={ data.symbol0 === 'Ether' }
noCopy
noSymbol
w="auto"
columnGap={ 1 }
logoSize={ 5 }
isDisabled={ data.symbol0 === 'Ether' }
hideSymbol
maxW="200px"
flexShrink={ 0 }
/>
<chakra.span color="text_secondary">{ type === 'swap' ? 'For' : 'And' }: </chakra.span>
<chakra.span fontWeight={ 600 }>{ amount1 }</chakra.span>
<TokenSnippet
data={ token1 }
<chakra.span fontWeight={ 600 } mr={ 1 }>{ amount1 }</chakra.span>
<TokenEntity
token={ token1 }
noLink={ data.symbol1 === 'Ether' }
noCopy
noSymbol
w="auto"
columnGap={ 1 }
logoSize={ 5 }
isDisabled={ data.symbol1 === 'Ether' }
hideSymbol
maxW="200px"
flexShrink={ 0 }
/>
<chakra.span color="text_secondary">{ text1 } </chakra.span>
<Flex columnGap={ 1 }>
<chakra.span color="text_secondary" mr={ 1 }>{ text1 }</chakra.span>
<Flex columnGap={ 2 }>
<Icon as={ uniswapIcon } boxSize={ 5 } color="white" bgColor="#ff007a" borderRadius="full" p="2px"/>
<chakra.span color="text_secondary">Uniswap V3</chakra.span>
</Flex>
......@@ -105,16 +103,20 @@ const TxDetailsAction = ({ action }: Props) => {
return (
<div>
<Flex rowGap={ 2 } flexWrap="wrap" alignItems="center" whiteSpace="pre-wrap">
<chakra.span>Mint of </chakra.span>
<TokenSnippet
data={ token }
<chakra.span mr={ 2 }>Mint of</chakra.span>
<TokenEntity
token={ token }
noCopy
w="auto"
columnGap={ 1 }
logoSize={ 5 }
rowGap={ 2 }
/>
<chakra.span> to </chakra.span>
<AddressLink hash={ data.to } type="address" truncation="constant"/>
<AddressEntity
address={{ hash: data.to }}
truncation="constant"
noIcon
noCopy
/>
</Flex>
<Flex columnGap={ 1 } rowGap={ 2 } pl={ 3 } flexDirection="column" mt={ 2 }>
......
......@@ -5,9 +5,9 @@ import type { TokenTransfer as TTokenTransfer, Erc20TotalPayload, Erc721TotalPay
import rightArrowIcon from 'icons/arrows/east.svg';
import { space } from 'lib/html-entities';
import AddressLink from 'ui/shared/address/AddressLink';
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';
interface Props {
......@@ -25,12 +25,11 @@ const TxDetailsTokenTransfer = ({ data }: Props) => {
<Text fontWeight={ 500 } as="span">For:{ space }
<CurrencyValue value={ total.value } exchangeRate={ data.token.exchange_rate } fontWeight={ 600 } decimals={ total.decimals }/>
</Text>
<TokenSnippet
data={ data.token }
<TokenEntity
token={ data.token }
noCopy
w="auto"
flexGrow="1"
columnGap={ 1 }
logoSize={ 5 }
/>
</Flex>
);
......@@ -63,17 +62,17 @@ const TxDetailsTokenTransfer = ({ data }: Props) => {
return (
<Flex
alignItems="center"
alignItems="flex-start"
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 }
rowGap={ 3 }
flexDir="row"
w="100%"
>
<Flex alignItems="center">
<AddressLink type="address" fontWeight="500" hash={ data.from.hash } truncation="constant"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/>
<AddressLink type="address" fontWeight="500" hash={ data.to.hash } truncation="constant"/>
<Flex alignItems="center" fontWeight="500">
<AddressEntity address={ data.from } truncation="constant" noIcon maxW="150px"/>
<Icon as={ rightArrowIcon } boxSize={ 5 } mx={ 2 } color="gray.500"/>
<AddressEntity address={ data.to } truncation="constant" noIcon maxW="150px"/>
</Flex>
<Flex flexDir="column" rowGap={ 5 } w="100%" overflow="hidden">
{ content }
......
......@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
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 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 TxStatus from 'ui/shared/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
......@@ -28,19 +25,19 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit:
{ typeTitle && <Tag colorScheme="cyan" isLoading={ isLoading }>{ typeTitle }</Tag> }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
</Flex>
<Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isLoading={ isLoading }/>
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/>
</Address>
<Box w="100%" display="flex" columnGap={ 3 } fontWeight="500">
<AddressEntity
address={ from }
isLoading={ isLoading }
width="calc((100% - 48px) / 2)"
/>
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
{ toData && (
<Address width="calc((100% - 48px) / 2)">
<AddressIcon address={ toData } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isLoading={ isLoading }/>
<CopyToClipboard text={ toData.hash } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ toData }
isLoading={ isLoading }
width="calc((100% - 48px) / 2)"
/>
) }
</Box>
<HStack spacing={ 3 }>
......
......@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
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 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 { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
......@@ -36,22 +33,20 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit:
</Flex>
</Td>
<Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } alias={ from.name } flexGrow={ 1 } isLoading={ isLoading }/>
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ from }
isLoading={ isLoading }
/>
</Td>
<Td px={ 0 } verticalAlign="middle">
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
</Td>
<Td verticalAlign="middle">
{ toData && (
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ toData } isLoading={ isLoading }/>
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isLoading={ isLoading }/>
<CopyToClipboard text={ toData.hash } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ toData }
isLoading={ isLoading }
/>
) }
</Td>
<Td isNumeric verticalAlign="middle">
......
......@@ -2,9 +2,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import { getStateElements } from './utils';
......@@ -22,12 +20,12 @@ const TxStateListItem = ({ data, isLoading }: Props) => {
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<Address flexGrow={ 1 } w="100%" alignSelf="center">
<AddressIcon address={ data.address } isLoading={ isLoading }/>
<AddressLink type="address" hash={ data.address.hash } ml={ 2 } truncation="constant" mr={ 3 } isLoading={ isLoading }/>
<ListItemMobileGrid.Value py="3px" display="flex" flexWrap="nowrap" columnGap={ 3 }>
<AddressEntity
address={ data.address }
isLoading={ isLoading }
/>
{ tag }
</Address>
</ListItemMobileGrid.Value>
{ before && (
......
......@@ -3,9 +3,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import { getStateElements } from './utils';
......@@ -25,18 +23,12 @@ const TxStateTableItem = ({ data, isLoading }: Props) => {
</Box>
</Td>
<Td>
<Address py="3px">
<AddressIcon address={ data.address } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ data.address.hash }
alias={ data.address.name }
fontWeight="500"
truncation="constant"
ml={ 2 }
<AddressEntity
address={ data.address }
isLoading={ isLoading }
truncation="constant"
py="7px"
/>
</Address>
</Td>
<Td isNumeric><Box py="7px">{ before }</Box></Td>
<Td isNumeric><Box py="7px">{ after }</Box></Td>
......
......@@ -8,8 +8,8 @@ import config from 'configs/app';
import { ZERO_ADDRESS } from 'lib/consts';
import { nbsp, space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxStateTokenIdList from './TxStateTokenIdList';
......@@ -77,11 +77,13 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
}
case 'token': {
const tokenLink = (
<AddressLink
type="token"
hash={ data.token.address }
alias={ data.token?.symbol || data.token.address }
<TokenEntity
token={ data.token }
isLoading={ isLoading }
noIcon
noCopy
onlySymbol
w="auto"
/>
);
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) {
return {
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>
<span>{ space }</span>
{ tokenLink }
</Flex>
) : null,
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>
<span>{ space }</span>
{ tokenLink }
......
......@@ -12,11 +12,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
......@@ -91,19 +88,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Flex>
) }
<Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isOut }
<AddressEntity
address={ tx.from }
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) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mx={ 2 } isLoading={ isLoading }/> : (
<Box mx={ 2 }>
......@@ -116,19 +108,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Box>
) }
{ dataTo ? (
<Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isIn }
<AddressEntity
address={ dataTo }
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>
<Box mt={ 2 }>
......
......@@ -15,13 +15,10 @@ import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg';
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 Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import CurrencyValue from 'ui/shared/CurrencyValue';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag';
......@@ -46,36 +43,27 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement);
const addressFrom = (
<Address w="100%">
<AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500" ml={ 2 }
truncation="constant"
isDisabled={ isOut }
<AddressEntity
address={ tx.from }
isLoading={ isLoading }
noCopy={ isOut }
noLink={ isOut }
truncation="constant"
w="100%"
py="2px"
/>
{ !isOut && <CopyToClipboard text={ tx.from.hash } isLoading={ isLoading }/> }
</Address>
);
const addressTo = dataTo ? (
<Address w="100%">
<AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
truncation="constant"
isDisabled={ isIn }
<AddressEntity
address={ dataTo }
isLoading={ isLoading }
truncation="constant"
noCopy={ isIn }
noLink={ isIn }
w="100%"
py="2px"
/>
{ !isIn && <CopyToClipboard text={ dataTo.hash } isLoading={ isLoading }/> }
</Address>
) : '-';
return (
......
......@@ -5,12 +5,12 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import editIcon from 'icons/edit.svg';
import dayjs from 'lib/date/dayjs';
import AddressSnippet from 'ui/shared/AddressSnippet';
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 VerifiedAddressesStatus from './VerifiedAddressesStatus';
import VerifiedAddressesTokenSnippet from './VerifiedAddressesTokenSnippet';
interface Props {
item: VerifiedAddress;
......@@ -48,9 +48,21 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
return <Link onClick={ handleAddClick }>Add details</Link>;
}
const token = {
icon_url: application.iconUrl,
address: application.tokenAddress,
name: item.metadata.tokenName,
symbol: '',
};
return (
<>
<VerifiedAddressesTokenSnippet application={ application } name={ item.metadata.tokenName }/>
<TokenEntity
token={ token }
noLink={ application.status === 'IN_PROCESS' }
noCopy
noSymbol
/>
<Tooltip label="Edit">
<IconButton
aria-label="edit"
......@@ -69,14 +81,18 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px">
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }} isLoading={ isLoading }/>
<ListItemMobileGrid.Value>
<AddressEntity
address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}
isLoading={ isLoading }
w="100%"
/>
</ListItemMobileGrid.Value>
{ item.metadata.tokenName && (
<>
<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 }
</ListItemMobileGrid.Value>
</>
......
......@@ -5,11 +5,11 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import editIcon from 'icons/edit.svg';
import dayjs from 'lib/date/dayjs';
import AddressSnippet from 'ui/shared/AddressSnippet';
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 VerifiedAddressesTokenSnippet from './VerifiedAddressesTokenSnippet';
interface Props {
item: VerifiedAddress;
......@@ -48,13 +48,31 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin
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 (
<Tr>
<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 fontSize="sm" verticalAlign="middle" pr={ 1 }>
{ 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';
import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShorten from 'ui/shared/HashStringShorten';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
......@@ -29,14 +27,18 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
return (
<ListItemMobile rowGap={ 3 }>
<Address columnGap={ 2 } overflow="hidden" w="100%">
<AddressIcon address={ data.address } isLoading={ isLoading }/>
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading } query={{ tab: 'contract' }}/>
<Flex w="100%">
<AddressEntity
isLoading={ isLoading }
address={ data.address }
query={{ tab: 'contract' }}
noCopy
/>
<Skeleton isLoaded={ !isLoading } color="text_secondary" ml="auto">
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
</Skeleton>
<CopyToClipboard text={ data.address.hash } ml={ -1 } isLoading={ isLoading }/>
</Address>
<CopyToClipboard text={ data.address.hash } isLoading={ isLoading }/>
</Flex>
<Flex columnGap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { config.chain.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
......
......@@ -9,10 +9,9 @@ import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg';
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 CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShorten from 'ui/shared/HashStringShorten';
interface Props {
......@@ -28,18 +27,19 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
return (
<Tr>
<Td>
<Flex columnGap={ 2 }>
<AddressIcon address={ data.address } isLoading={ isLoading }/>
<Flex columnGap={ 2 } flexWrap="wrap" w="calc(100% - 32px)">
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading } my={ 1 } query={{ tab: 'contract' }}/>
<Flex alignItems="center">
<AddressEntity
address={ data.address }
isLoading={ isLoading }
query={{ tab: 'contract' }}
noCopy
mt={ 1 }
/>
<Flex alignItems="center" ml={ 7 }>
<Skeleton isLoaded={ !isLoading } color="text_secondary" my={ 1 }>
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
</Skeleton>
<CopyToClipboard text={ data.address.hash } isLoading={ isLoading }/>
</Flex>
</Flex>
</Flex>
</Td>
<Td isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block" my={ 1 }>
......
......@@ -9,10 +9,10 @@ import TokensIcon from 'icons/tokens.svg';
import WalletIcon from 'icons/wallet.svg';
import getCurrencyValue from 'lib/getCurrencyValue';
import { nbsp } from 'lib/html-entities';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Icon from 'ui/shared/chakra/Icon';
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 infoItemsPaddingLeft = { base: 1, lg: 8 };
......@@ -20,19 +20,23 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL
const nativeTokenData = React.useMemo(() => ({
name: config.chain.currency.name || '',
icon_url: '',
symbol: '',
address: '',
}), [ ]);
const { usdBn: usdNative } = getCurrencyValue({ value: item.address_balance, accuracy: 2, accuracyUsd: 2, exchangeRate: item.exchange_rate });
return (
<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 }>
<TokenLogo
data={ nativeTokenData }
boxSize={ 5 }
borderRadius="sm"
mr={ 2 }
<TokenEntity.Icon
token={ nativeTokenData }
isLoading={ isLoading }
/>
<Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="inline-flex">
......
......@@ -7,10 +7,8 @@ import type { WithdrawalsItem } from 'types/api/withdrawals';
import config from 'configs/app';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
......@@ -62,10 +60,10 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => {
{ view !== 'address' && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>To</ListItemMobileGrid.Label><ListItemMobileGrid.Value>
<Address>
<AddressIcon address={ item.receiver } isLoading={ isLoading }/>
<AddressLink type="address" hash={ item.receiver.hash } truncation="dynamic" ml={ 2 } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ item.receiver }
isLoading={ isLoading }
/>
</ListItemMobileGrid.Value>
</>
) }
......
......@@ -6,10 +6,8 @@ import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals';
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 AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
type Props = ({
......@@ -44,10 +42,11 @@ const WithdrawalsTableItem = ({ item, view, isLoading }: Props) => {
) }
{ view !== 'address' && (
<Td verticalAlign="middle">
<Address>
<AddressIcon address={ item.receiver } isLoading={ isLoading }/>
<AddressLink type="address" hash={ item.receiver.hash } truncation="constant" ml={ 2 } isLoading={ isLoading }/>
</Address>
<AddressEntity
address={ item.receiver }
isLoading={ isLoading }
truncation="constant"
/>
</Td>
) }
{ 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