Commit 04f051f2 authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Merge pull request #1912 from blockscout/fe-1802

Multi chain balance button
parents 4e2f5b7f ae4c59ac
...@@ -15,6 +15,7 @@ export { default as growthBook } from './growthBook'; ...@@ -15,6 +15,7 @@ export { default as growthBook } from './growthBook';
export { default as marketplace } from './marketplace'; export { default as marketplace } from './marketplace';
export { default as metasuites } from './metasuites'; export { default as metasuites } from './metasuites';
export { default as mixpanel } from './mixpanel'; export { default as mixpanel } from './mixpanel';
export { default as multichainButton } from './multichainButton';
export { default as nameService } from './nameService'; export { default as nameService } from './nameService';
export { default as restApiDocs } from './restApiDocs'; export { default as restApiDocs } from './restApiDocs';
export { default as rollup } from './rollup'; export { default as rollup } from './rollup';
......
import type { Feature } from './types';
import type { MultichainProviderConfig } from 'types/client/multichainProviderConfig';
import { getEnvValue, parseEnvJson } from '../utils';
import marketplace from './marketplace';
const value = parseEnvJson<MultichainProviderConfig>(getEnvValue('NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG'));
const title = 'Multichain balance';
const config: Feature<{name: string; logoUrl?: string; urlTemplate: string; dappId?: string }> = (() => {
if (value) {
return Object.freeze({
title,
isEnabled: true,
name: value.name,
logoUrl: value.logo,
urlTemplate: value.url_template,
dappId: marketplace.isEnabled ? value.dapp_id : undefined,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
...@@ -54,8 +54,10 @@ NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKj ...@@ -54,8 +54,10 @@ NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKj
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=gearbox-protocol NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=gearbox-protocol
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
#meta #meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
...@@ -15,6 +15,7 @@ import type { ContractCodeIde } from '../../../types/client/contract'; ...@@ -15,6 +15,7 @@ import type { ContractCodeIde } from '../../../types/client/contract';
import { GAS_UNITS } from '../../../types/client/gasTracker'; import { GAS_UNITS } from '../../../types/client/gasTracker';
import type { GasUnit } from '../../../types/client/gasTracker'; import type { GasUnit } from '../../../types/client/gasTracker';
import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace'; import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace';
import type { MultichainProviderConfig } from '../../../types/client/multichainProviderConfig';
import { NAVIGATION_LINK_IDS } from '../../../types/client/navigation-items'; import { NAVIGATION_LINK_IDS } from '../../../types/client/navigation-items';
import type { NavItemExternal, NavigationLinkId } from '../../../types/client/navigation-items'; import type { NavItemExternal, NavigationLinkId } from '../../../types/client/navigation-items';
import { ROLLUP_TYPES } from '../../../types/client/rollup'; import { ROLLUP_TYPES } from '../../../types/client/rollup';
...@@ -620,6 +621,19 @@ const schema = yup ...@@ -620,6 +621,19 @@ const schema = yup
NEXT_PUBLIC_HAS_USER_OPS: yup.boolean(), NEXT_PUBLIC_HAS_USER_OPS: yup.boolean(),
NEXT_PUBLIC_METASUITES_ENABLED: yup.boolean(), NEXT_PUBLIC_METASUITES_ENABLED: yup.boolean(),
NEXT_PUBLIC_SWAP_BUTTON_URL: yup.string(), NEXT_PUBLIC_SWAP_BUTTON_URL: yup.string(),
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG: yup
.mixed()
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG, it should have name and url template', (data) => {
const isUndefined = data === undefined;
const valueSchema = yup.object<MultichainProviderConfig>().transform(replaceQuotes).json().shape({
name: yup.string().required(),
url_template: yup.string().required(),
logo: yup.string(),
dapp_id: yup.string(),
});
return isUndefined || valueSchema.isValidSync(data);
}),
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE: yup.string<ValidatorsChainType>().oneOf(VALIDATORS_CHAIN_TYPE), NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE: yup.string<ValidatorsChainType>().oneOf(VALIDATORS_CHAIN_TYPE),
NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(), NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(),
NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)), NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)),
......
...@@ -74,3 +74,5 @@ NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false ...@@ -74,3 +74,5 @@ NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false
NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket'] NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket']
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
...@@ -57,6 +57,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -57,6 +57,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring) - [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [OpenTelemetry](ENVS.md#opentelemetry) - [OpenTelemetry](ENVS.md#opentelemetry)
- [Swap button](ENVS.md#swap-button) - [Swap button](ENVS.md#swap-button)
- [Multichain balance button](ENVS.md#multichain-button)
- [3rd party services configuration](ENVS.md#external-services-configuration) - [3rd party services configuration](ENVS.md#external-services-configuration)
&nbsp; &nbsp;
...@@ -679,6 +680,27 @@ If the feature is enabled, a Swap button will be displayed at the top of the exp ...@@ -679,6 +680,27 @@ If the feature is enabled, a Swap button will be displayed at the top of the exp
&nbsp; &nbsp;
### Multichain balance button
If the feature is enabled, a Multichain balance button will be displayed on the address page, which will take you to the portfolio application in the marketplace or to an external site.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `{ name: string; url_template: string; dapp_id?: string; logo?: string }` | Multichain portfolio application config See [below](#multichain-button-configuration-properties) | - | - | `{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'` |
&nbsp;
#### Multichain button configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| name | `string` | Multichain portfolio application name | Required | - | `zerion` |
| url_template | `string` | Url template to the portfolio. Should be a template with `{address}` variable | Required | - | `https://app.zerion.io/{address}/overview` |
| dapp_id | `string` | Set for open a Blockscout dapp page with the portfolio instead of opening external app page | - | - | `zerion` |
| logo | `string` | Multichain portfolio application logo (.svg) url | - | - | `https://example.com/icon.svg` |
&nbsp;
## External services configuration ## External services configuration
### Google ReCaptcha ### Google ReCaptcha
......
...@@ -128,7 +128,7 @@ Type extends EventTypes.FILTERS ? { ...@@ -128,7 +128,7 @@ Type extends EventTypes.FILTERS ? {
'Filter name': string; 'Filter name': string;
} : } :
Type extends EventTypes.BUTTON_CLICK ? { Type extends EventTypes.BUTTON_CLICK ? {
'Content': 'Swap button'; 'Content': 'Swap button' | 'Multichain';
'Source': string; 'Source': string;
} : } :
Type extends EventTypes.PROMO_BANNER ? { Type extends EventTypes.PROMO_BANNER ? {
......
...@@ -75,7 +75,7 @@ export const token: Address = { ...@@ -75,7 +75,7 @@ export const token: Address = {
coin_balance: '1', coin_balance: '1',
creation_tx_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98', creation_tx_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98',
creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72', creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72',
exchange_rate: null, exchange_rate: '0.04311',
implementation_address: null, implementation_address: null,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: false, has_logs: false,
......
export type MultichainProviderConfig = {
name: string;
dapp_id?: string;
url_template: string;
logo?: string;
};
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props { interface Props {
address: string; address: string;
......
...@@ -2,6 +2,7 @@ import { Box, Text, Grid } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { Box, Text, Grid } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import config from 'configs/app';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import useIsMounted from 'lib/hooks/useIsMounted'; import useIsMounted from 'lib/hooks/useIsMounted';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -17,6 +18,7 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity'; ...@@ -17,6 +18,7 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity';
import AddressBalance from './details/AddressBalance'; import AddressBalance from './details/AddressBalance';
import AddressNameInfo from './details/AddressNameInfo'; import AddressNameInfo from './details/AddressNameInfo';
import AddressNetWorth from './details/AddressNetWorth';
import TokenSelect from './tokenSelect/TokenSelect'; import TokenSelect from './tokenSelect/TokenSelect';
import useAddressCountersQuery from './utils/useAddressCountersQuery'; import useAddressCountersQuery from './utils/useAddressCountersQuery';
import type { AddressQuery } from './utils/useAddressQuery'; import type { AddressQuery } from './utils/useAddressQuery';
...@@ -129,6 +131,17 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -129,6 +131,17 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> } { addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> }
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
{ (config.features.multichainButton.isEnabled || (data.exchange_rate && data.has_tokens)) && (
<DetailsInfoItem
title="Net worth"
hint="Total net worth in USD of all tokens for the address"
alignSelf="center"
isLoading={ addressQuery.isPlaceholderData }
>
<AddressNetWorth addressData={ addressQuery.data } addressHash={ addressHash } isLoading={ addressQuery.isPlaceholderData }/>
</DetailsInfoItem>
)
}
<DetailsInfoItem <DetailsInfoItem
title="Transactions" title="Transactions"
hint="Number of transactions related to this address" hint="Number of transactions related to this address"
......
...@@ -7,7 +7,7 @@ import React from 'react'; ...@@ -7,7 +7,7 @@ import React from 'react';
import solidityScanIcon from 'icons/brands/solidity_scan.svg'; import solidityScanIcon from 'icons/brands/solidity_scan.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { SOLIDITYSCAN_REPORT } from 'stubs/contract'; import { SOLIDITYSCAN_REPORT } from 'stubs/contract';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton'; import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails'; import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails';
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore'; import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
......
...@@ -5,7 +5,7 @@ import type { NovesResponseData } from 'types/api/noves'; ...@@ -5,7 +5,7 @@ import type { NovesResponseData } from 'types/api/noves';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import NovesFromTo from 'ui/shared/Noves/NovesFromTo';
......
...@@ -5,7 +5,7 @@ import type { NovesResponseData } from 'types/api/noves'; ...@@ -5,7 +5,7 @@ import type { NovesResponseData } from 'types/api/noves';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import NovesFromTo from 'ui/shared/Noves/NovesFromTo';
type Props = { type Props = {
......
...@@ -7,7 +7,7 @@ import type { FormSubmitResultWalletClient } from '../types'; ...@@ -7,7 +7,7 @@ import type { FormSubmitResultWalletClient } from '../types';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props { interface Props {
result: FormSubmitResultWalletClient['result']; result: FormSubmitResultWalletClient['result'];
......
...@@ -20,8 +20,8 @@ import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; ...@@ -20,8 +20,8 @@ import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
import ContractSecurityAudits from './ContractSecurityAudits'; import ContractSecurityAudits from './ContractSecurityAudits';
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
interface Props { interface Props {
className?: string; className?: string;
......
...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY'; import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
import FormModal from 'ui/shared/FormModal'; import FormModal from 'ui/shared/FormModal';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import ContractSubmitAuditForm from './contractSubmitAuditForm/ContractSubmitAuditForm'; import ContractSubmitAuditForm from './contractSubmitAuditForm/ContractSubmitAuditForm';
......
...@@ -9,7 +9,7 @@ import { route } from 'nextjs-routes'; ...@@ -9,7 +9,7 @@ import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import * as stubs from 'stubs/contract'; import * as stubs from 'stubs/contract';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import CodeEditor from 'ui/shared/monaco/CodeEditor'; import CodeEditor from 'ui/shared/monaco/CodeEditor';
import formatFilePath from 'ui/shared/monaco/utils/formatFilePath'; import formatFilePath from 'ui/shared/monaco/utils/formatFilePath';
......
...@@ -67,7 +67,7 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -67,7 +67,7 @@ const AddressBalance = ({ data, isLoading }: Props) => {
return ( return (
<DetailsInfoItem <DetailsInfoItem
title="Balance" title={ `${ currencyUnits.ether } balance` }
hint={ `Address balance in ${ currencyUnits.ether }. Doesn't include ERC20, ERC721 and ERC1155 tokens` } hint={ `Address balance in ${ currencyUnits.ether }. Doesn't include ERC20, ERC721 and ERC1155 tokens` }
flexWrap="nowrap" flexWrap="nowrap"
alignSelf="center" alignSelf="center"
......
...@@ -8,7 +8,7 @@ import type { AddressCounters } from 'types/api/address'; ...@@ -8,7 +8,7 @@ import type { AddressCounters } from 'types/api/address';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props { interface Props {
prop: keyof AddressCounters; prop: keyof AddressCounters;
......
import React from 'react';
import * as addressMock from 'mocks/address/address';
import * as tokensMock from 'mocks/address/tokens';
import { test, expect } from 'playwright/lib';
import AddressNetWorth from './AddressNetWorth';
const ADDRESS_HASH = addressMock.hash;
const ICON_URL = 'https://localhost:3000/my-icon.png';
test.beforeEach(async({ mockApiResponse }) => {
await mockApiResponse('address_tokens', tokensMock.erc20List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } });
await mockApiResponse('address_tokens', tokensMock.erc721List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' } });
await mockApiResponse('address_tokens', tokensMock.erc1155List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' } });
await mockApiResponse('address_tokens', tokensMock.erc404List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' } });
});
test('base view', async({ render }) => {
const component = await render(<AddressNetWorth addressData={ addressMock.token } addressHash={ ADDRESS_HASH }/>);
await expect(component).toHaveScreenshot();
});
test('with multichain button internal +@dark-mode', async({ render, mockEnvs, mockAssetResponse }) => {
await mockEnvs([
[
'NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG',
`{"name": "duck", "dapp_id": "duck", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL }"}` ],
]);
await mockAssetResponse(ICON_URL, './playwright/mocks/image_svg.svg');
const component = await render(<AddressNetWorth addressData={ addressMock.token } addressHash={ ADDRESS_HASH }/>);
await expect(component).toHaveScreenshot();
});
test('with multichain button external', async({ render, mockEnvs, mockAssetResponse }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG', `{"name": "duck", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL }"}` ],
]);
await mockAssetResponse(ICON_URL, './playwright/mocks/image_svg.svg');
const component = await render(<AddressNetWorth addressData={ addressMock.token } addressHash={ ADDRESS_HASH }/>);
await expect(component).toHaveScreenshot();
});
import { Image, Skeleton, Text } from '@chakra-ui/react';
import _capitalize from 'lodash/capitalize';
import React from 'react';
import type { Address } from 'types/api/address';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import getCurrencyValue from 'lib/getCurrencyValue';
import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/links/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator';
import { getTokensTotalInfo } from '../utils/tokenUtils';
import useFetchTokens from '../utils/useFetchTokens';
const TEMPLATE_ADDRESS = '{address}';
const multichainFeature = config.features.multichainButton;
type Props = {
addressHash: string;
addressData?: Address;
isLoading?: boolean;
}
const AddressNetWorth = ({ addressData, isLoading, addressHash }: Props) => {
const { data, isError, isPending } = useFetchTokens({ hash: addressData?.hash, enabled: addressData?.has_tokens });
const { usdBn: nativeUsd } = getCurrencyValue({
value: addressData?.coin_balance || '0',
accuracy: 8,
accuracyUsd: 2,
exchangeRate: addressData?.exchange_rate,
decimals: String(config.chain.currency.decimals),
});
const { usd, isOverflow } = getTokensTotalInfo(data);
const prefix = isOverflow ? '>' : '';
const totalUsd = nativeUsd.plus(usd);
const onMultichainClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Multichain', Source: 'address' });
}, []);
let multichainItem = null;
if (multichainFeature.isEnabled) {
const buttonContent = (
<>
{ multichainFeature.logoUrl &&
<Image src={ multichainFeature.logoUrl } alt={ multichainFeature.name } boxSize={ 5 } mr={ 2 } borderRadius="4px" overflow="hidden"/>
}
{ _capitalize(multichainFeature.name) }</>
);
const linkProps = {
variant: 'subtle' as const,
display: 'flex',
alignItems: 'center',
fontSize: 'sm',
fontWeight: 500,
onClick: onMultichainClick,
};
try {
const portfolioUrlString = multichainFeature.urlTemplate.replace(TEMPLATE_ADDRESS, addressHash);
const portfolioUrl = new URL(portfolioUrlString);
portfolioUrl.searchParams.append('utm_source', 'blockscout');
portfolioUrl.searchParams.append('utm_medium', 'address');
const dappId = multichainFeature.dappId;
multichainItem = (
<>
<TextSeparator mx={ 3 } color="gray.500"/>
<Text mr={ 2 }>Multichain</Text>
{ typeof dappId === 'string' ? (
<LinkInternal
href={ route({ pathname: '/apps/[id]', query: { id: dappId, url: portfolioUrl.toString() } }) }
{ ...linkProps }
>
{ buttonContent }
</LinkInternal>
) : (
<LinkExternal
href={ portfolioUrl.toString() }
{ ...linkProps }
>
{ buttonContent }
</LinkExternal>
) }
</>
);
} catch (error) {}
}
return (
<Skeleton display="flex" alignItems="center" isLoaded={ !isLoading && !(addressData?.has_tokens && isPending) }>
<Text>
{ (isError || !addressData?.exchange_rate) ? 'N/A' : `${ prefix }$${ totalUsd.toFormat(2) }` }
</Text>
{ multichainItem }
</Skeleton>
);
};
export default AddressNetWorth;
...@@ -25,7 +25,7 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -25,7 +25,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip'; import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip';
interface Props { interface Props {
......
...@@ -6,7 +6,7 @@ import { route } from 'nextjs-routes'; ...@@ -6,7 +6,7 @@ import { route } from 'nextjs-routes';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
import type { TokenEnhancedData } from '../utils/tokenUtils'; import type { TokenEnhancedData } from '../utils/tokenUtils';
......
...@@ -8,7 +8,7 @@ import { apos } from 'lib/html-entities'; ...@@ -8,7 +8,7 @@ import { apos } from 'lib/html-entities';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import NftFallback from 'ui/shared/nft/NftFallback'; import NftFallback from 'ui/shared/nft/NftFallback';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
......
...@@ -12,6 +12,7 @@ import useSocketMessage from 'lib/socket/useSocketMessage'; ...@@ -12,6 +12,7 @@ import useSocketMessage from 'lib/socket/useSocketMessage';
import { calculateUsdValue } from './tokenUtils'; import { calculateUsdValue } from './tokenUtils';
interface Props { interface Props {
hash?: string; hash?: string;
enabled?: boolean;
} }
const tokenBalanceItemIdentityFactory = (match: AddressTokenBalance) => (item: AddressTokenBalance) => (( const tokenBalanceItemIdentityFactory = (match: AddressTokenBalance) => (item: AddressTokenBalance) => ((
...@@ -20,26 +21,26 @@ const tokenBalanceItemIdentityFactory = (match: AddressTokenBalance) => (item: A ...@@ -20,26 +21,26 @@ const tokenBalanceItemIdentityFactory = (match: AddressTokenBalance) => (item: A
match.token_instance?.id === item.token_instance?.id match.token_instance?.id === item.token_instance?.id
)); ));
export default function useFetchTokens({ hash }: Props) { export default function useFetchTokens({ hash, enabled }: Props) {
const erc20query = useApiQuery('address_tokens', { const erc20query = useApiQuery('address_tokens', {
pathParams: { hash }, pathParams: { hash },
queryParams: { type: 'ERC-20' }, queryParams: { type: 'ERC-20' },
queryOptions: { enabled: Boolean(hash), refetchOnMount: false }, queryOptions: { enabled: Boolean(hash) && enabled, refetchOnMount: false },
}); });
const erc721query = useApiQuery('address_tokens', { const erc721query = useApiQuery('address_tokens', {
pathParams: { hash }, pathParams: { hash },
queryParams: { type: 'ERC-721' }, queryParams: { type: 'ERC-721' },
queryOptions: { enabled: Boolean(hash), refetchOnMount: false }, queryOptions: { enabled: Boolean(hash) && enabled, refetchOnMount: false },
}); });
const erc1155query = useApiQuery('address_tokens', { const erc1155query = useApiQuery('address_tokens', {
pathParams: { hash }, pathParams: { hash },
queryParams: { type: 'ERC-1155' }, queryParams: { type: 'ERC-1155' },
queryOptions: { enabled: Boolean(hash), refetchOnMount: false }, queryOptions: { enabled: Boolean(hash) && enabled, refetchOnMount: false },
}); });
const erc404query = useApiQuery('address_tokens', { const erc404query = useApiQuery('address_tokens', {
pathParams: { hash }, pathParams: { hash },
queryParams: { type: 'ERC-404' }, queryParams: { type: 'ERC-404' },
queryOptions: { enabled: Boolean(hash), refetchOnMount: false }, queryOptions: { enabled: Boolean(hash) && enabled, refetchOnMount: false },
}); });
const queryClient = useQueryClient(); const queryClient = useQueryClient();
......
...@@ -16,7 +16,7 @@ import { route } from 'nextjs-routes'; ...@@ -16,7 +16,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import AdminSupportText from 'ui/shared/texts/AdminSupportText'; import AdminSupportText from 'ui/shared/texts/AdminSupportText';
import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress'; import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress';
......
...@@ -25,7 +25,7 @@ import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; ...@@ -25,7 +25,7 @@ import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import PrevNext from 'ui/shared/PrevNext'; import PrevNext from 'ui/shared/PrevNext';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
......
...@@ -17,7 +17,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity'; ...@@ -17,7 +17,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
......
...@@ -15,7 +15,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity'; ...@@ -15,7 +15,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
......
...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import { STATS_CHARTS } from 'stubs/stats'; import { STATS_CHARTS } from 'stubs/stats';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ChartWidgetContainer from 'ui/stats/ChartWidgetContainer'; import ChartWidgetContainer from 'ui/stats/ChartWidgetContainer';
const GAS_PRICE_CHART_ID = 'averageGasPrice'; const GAS_PRICE_CHART_ID = 'averageGasPrice';
......
...@@ -16,7 +16,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel'; ...@@ -16,7 +16,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import LatestBlocksItem from './LatestBlocksItem'; import LatestBlocksItem from './LatestBlocksItem';
......
...@@ -11,7 +11,7 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -11,7 +11,7 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { L2_DEPOSIT_ITEM } from 'stubs/L2'; import { L2_DEPOSIT_ITEM } from 'stubs/L2';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import LatestDepositsItem from './LatestDepositsItem'; import LatestDepositsItem from './LatestDepositsItem';
......
...@@ -8,7 +8,7 @@ import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; ...@@ -8,7 +8,7 @@ import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket'; import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import LatestTxsItem from './LatestTxsItem'; import LatestTxsItem from './LatestTxsItem';
......
...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -7,7 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import LatestTxsItem from './LatestTxsItem'; import LatestTxsItem from './LatestTxsItem';
import LatestTxsItemMobile from './LatestTxsItemMobile'; import LatestTxsItemMobile from './LatestTxsItemMobile';
......
...@@ -13,7 +13,7 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -13,7 +13,7 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { ZKEVM_L2_TXN_BATCHES_ITEM } from 'stubs/zkEvmL2'; import { ZKEVM_L2_TXN_BATCHES_ITEM } from 'stubs/zkEvmL2';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import LatestZkevmL2BatchItem from './LatestZkevmL2BatchItem'; import LatestZkevmL2BatchItem from './LatestZkevmL2BatchItem';
......
...@@ -12,7 +12,7 @@ import { route } from 'nextjs-routes'; ...@@ -12,7 +12,7 @@ import { route } from 'nextjs-routes';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus'; import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
type Props = { type Props = {
......
...@@ -5,7 +5,7 @@ import type { SolidityscanReport } from 'types/api/contract'; ...@@ -5,7 +5,7 @@ import type { SolidityscanReport } from 'types/api/contract';
import config from 'configs/app'; import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton'; import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails'; import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails';
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore'; import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
......
...@@ -6,7 +6,7 @@ import config from 'configs/app'; ...@@ -6,7 +6,7 @@ import config from 'configs/app';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import EmptySearchResultDefault from 'ui/shared/EmptySearchResult'; import EmptySearchResultDefault from 'ui/shared/EmptySearchResult';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
const feature = config.features.marketplace; const feature = config.features.marketplace;
......
...@@ -10,8 +10,8 @@ import config from 'configs/app'; ...@@ -10,8 +10,8 @@ import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop'; import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop'; import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop';
......
...@@ -12,7 +12,7 @@ import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; ...@@ -12,7 +12,7 @@ import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import NftEntity from 'ui/shared/entities/nft/NftEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import NameDomainExpiryStatus from './NameDomainExpiryStatus'; import NameDomainExpiryStatus from './NameDomainExpiryStatus';
......
...@@ -17,7 +17,7 @@ import MarketplaceListWithScores from 'ui/marketplace/MarketplaceListWithScores' ...@@ -17,7 +17,7 @@ import MarketplaceListWithScores from 'ui/marketplace/MarketplaceListWithScores'
import FilterInput from 'ui/shared/filters/FilterInput'; import FilterInput from 'ui/shared/filters/FilterInput';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RadioButtonGroup from 'ui/shared/radioButtonGroup/RadioButtonGroup'; import RadioButtonGroup from 'ui/shared/radioButtonGroup/RadioButtonGroup';
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll'; import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
......
...@@ -17,7 +17,7 @@ import TextAd from 'ui/shared/ad/TextAd'; ...@@ -17,7 +17,7 @@ import TextAd from 'ui/shared/ad/TextAd';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton'; import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
......
...@@ -23,7 +23,7 @@ import TextAd from 'ui/shared/ad/TextAd'; ...@@ -23,7 +23,7 @@ import TextAd from 'ui/shared/ad/TextAd';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
......
...@@ -21,8 +21,8 @@ import * as TxEntity from 'ui/shared/entities/tx/TxEntity'; ...@@ -21,8 +21,8 @@ import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import * as UserOpEntity from 'ui/shared/entities/userOp/UserOpEntity'; import * as UserOpEntity from 'ui/shared/entities/userOp/UserOpEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
......
...@@ -21,8 +21,8 @@ import * as TxEntity from 'ui/shared/entities/tx/TxEntity'; ...@@ -21,8 +21,8 @@ import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import * as UserOpEntity from 'ui/shared/entities/userOp/UserOpEntity'; import * as UserOpEntity from 'ui/shared/entities/userOp/UserOpEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
......
...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes'; ...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from '../LinkExternal'; import LinkExternal from '../links/LinkExternal';
type Props = { type Props = {
data: NonNullable<AddressMetadataTagFormatted['meta']>; data: NonNullable<AddressMetadataTagFormatted['meta']>;
......
import type { LinkProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { EntityTag } from './types'; import type { EntityTag } from './types';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import { getTagLinkParams } from './utils'; import { getTagLinkParams } from './utils';
...@@ -30,7 +29,7 @@ const EntityTagLink = ({ data, children }: Props) => { ...@@ -30,7 +29,7 @@ const EntityTagLink = ({ data, children }: Props) => {
}); });
}, [ linkParams?.href, data.slug ]); }, [ linkParams?.href, data.slug ]);
const linkProps: LinkProps = { const linkProps = {
color: 'inherit', color: 'inherit',
display: 'inline-flex', display: 'inline-flex',
overflow: 'hidden', overflow: 'hidden',
......
...@@ -5,7 +5,7 @@ import type { EntityTag } from './types'; ...@@ -5,7 +5,7 @@ import type { EntityTag } from './types';
import makePrettyLink from 'lib/makePrettyLink'; import makePrettyLink from 'lib/makePrettyLink';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
interface Props { interface Props {
data: EntityTag; data: EntityTag;
......
...@@ -19,7 +19,7 @@ import type { NetworkExplorer as TNetworkExplorer } from 'types/networks'; ...@@ -19,7 +19,7 @@ import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import config from 'configs/app'; import config from 'configs/app';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import stripTrailingSlash from 'lib/stripTrailingSlash';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip'; import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip';
interface Props { interface Props {
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void }; type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void };
......
...@@ -8,8 +8,8 @@ import HashStringShorten from 'ui/shared/HashStringShorten'; ...@@ -8,8 +8,8 @@ import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import { getIconProps, type IconSize } from './utils'; import { getIconProps, type IconSize } from './utils';
......
...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes'; ...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import GasInfoTooltipRow from './GasInfoTooltipRow'; import GasInfoTooltipRow from './GasInfoTooltipRow';
import GasInfoUpdateTimer from './GasInfoUpdateTimer'; import GasInfoUpdateTimer from './GasInfoUpdateTimer';
......
import type { ChakraProps, LinkProps } from '@chakra-ui/react'; import type { LinkProps } from '@chakra-ui/react';
import { Link, chakra, Box, Skeleton, useColorModeValue } from '@chakra-ui/react'; import { Link, chakra, Box, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import type { Variants } from './useLinkStyles';
import { useLinkStyles } from './useLinkStyles';
interface Props { interface Props {
href: string; href: string;
className?: string; className?: string;
children: React.ReactNode; children: React.ReactNode;
isLoading?: boolean; isLoading?: boolean;
variant?: 'subtle'; variant?: Variants;
iconColor?: LinkProps['color']; iconColor?: LinkProps['color'];
onClick?: LinkProps['onClick']; onClick?: LinkProps['onClick'];
} }
const LinkExternal = ({ href, children, className, isLoading, variant, iconColor, onClick }: Props) => { const LinkExternal = ({ href, children, className, isLoading, variant, iconColor, onClick }: Props) => {
const subtleLinkBg = useColorModeValue('gray.100', 'gray.700'); const commonProps = {
display: 'inline-block',
const styleProps: ChakraProps = (() => { alignItems: 'center',
const commonProps = { };
display: 'inline-block',
alignItems: 'center',
};
switch (variant) { const styleProps = useLinkStyles(commonProps, variant);
case 'subtle': {
return {
...commonProps,
px: '10px',
py: '6px',
bgColor: subtleLinkBg,
borderRadius: 'base',
};
}
default:{
return commonProps;
}
}
})();
if (isLoading) { if (isLoading) {
if (variant === 'subtle') { if (variant === 'subtle') {
......
...@@ -5,18 +5,28 @@ import NextLink from 'next/link'; ...@@ -5,18 +5,28 @@ import NextLink from 'next/link';
import type { LegacyRef } from 'react'; import type { LegacyRef } from 'react';
import React from 'react'; import React from 'react';
const LinkInternal = ({ isLoading, ...props }: LinkProps & { isLoading?: boolean }, ref: LegacyRef<HTMLAnchorElement>) => { import type { Variants } from './useLinkStyles';
import { useLinkStyles } from './useLinkStyles';
type Props = LinkProps & {
variant?: Variants;
isLoading?: boolean;
}
const LinkInternal = ({ isLoading, variant, ...props }: Props, ref: LegacyRef<HTMLAnchorElement>) => {
const styleProps = useLinkStyles({}, variant);
if (isLoading) { if (isLoading) {
return <Flex alignItems="center" { ...props as FlexProps }>{ props.children }</Flex>; return <Flex alignItems="center" { ...props as FlexProps } { ...styleProps }>{ props.children }</Flex>;
} }
if (!props.href) { if (!props.href) {
return <Link { ...props } ref={ ref }/>; return <Link { ...props } ref={ ref } { ...styleProps }/>;
} }
return ( return (
<NextLink href={ props.href as NextLinkProps['href'] } passHref target={ props.target } legacyBehavior> <NextLink href={ props.href as NextLinkProps['href'] } passHref target={ props.target } legacyBehavior>
<Link { ...props } ref={ ref }/> <Link { ...props } ref={ ref } { ...styleProps }/>
</NextLink> </NextLink>
); );
}; };
......
import type { ChakraProps } from '@chakra-ui/react';
import { useColorModeValue } from '@chakra-ui/react';
export type Variants = 'subtle'
export function useLinkStyles(commonProps: ChakraProps, variant?: Variants) {
const subtleLinkBg = useColorModeValue('gray.100', 'gray.700');
switch (variant) {
case 'subtle': {
return {
...commonProps,
px: '10px',
py: '6px',
bgColor: subtleLinkBg,
borderRadius: 'base',
};
}
default:{
return commonProps;
}
}
}
...@@ -20,7 +20,7 @@ import { route } from 'nextjs-routes'; ...@@ -20,7 +20,7 @@ import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { getRecentSearchKeywords, saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { getRecentSearchKeywords, saveToRecentKeywords } from 'lib/recentSearchKeywords';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import SearchBarBackdrop from './SearchBarBackdrop'; import SearchBarBackdrop from './SearchBarBackdrop';
import SearchBarInput from './SearchBarInput'; import SearchBarInput from './SearchBarInput';
......
...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes'; ...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
......
...@@ -6,7 +6,7 @@ import type { TokenVerifiedInfo as TTokenVerifiedInfo } from 'types/api/token'; ...@@ -6,7 +6,7 @@ import type { TokenVerifiedInfo as TTokenVerifiedInfo } from 'types/api/token';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import TokenProjectInfo from './TokenProjectInfo'; import TokenProjectInfo from './TokenProjectInfo';
......
...@@ -7,7 +7,7 @@ import type { MetadataAttributes } from 'types/client/token'; ...@@ -7,7 +7,7 @@ import type { MetadataAttributes } from 'types/client/token';
import parseMetadata from 'lib/token/parseMetadata'; import parseMetadata from 'lib/token/parseMetadata';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
interface Props { interface Props {
......
...@@ -5,7 +5,7 @@ import { route } from 'nextjs-routes'; ...@@ -5,7 +5,7 @@ import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props { interface Props {
hash: string; hash: string;
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { Primitive } from 'react-hook-form'; import type { Primitive } from 'react-hook-form';
import urlParser from 'lib/token/metadata/urlParser'; import urlParser from 'lib/token/metadata/urlParser';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import MetadataAccordionItem from './MetadataAccordionItem'; import MetadataAccordionItem from './MetadataAccordionItem';
import MetadataAccordionItemTitle from './MetadataAccordionItemTitle'; import MetadataAccordionItemTitle from './MetadataAccordionItemTitle';
......
...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes'; ...@@ -7,7 +7,7 @@ import { route } from 'nextjs-routes';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import TxDetailsTokenTransfer from './TxDetailsTokenTransfer'; import TxDetailsTokenTransfer from './TxDetailsTokenTransfer';
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
...@@ -18,7 +18,7 @@ import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; ...@@ -18,7 +18,7 @@ import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp'; import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import PrevNext from 'ui/shared/PrevNext'; import PrevNext from 'ui/shared/PrevNext';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus'; import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus'; import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
...@@ -19,7 +19,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert'; ...@@ -19,7 +19,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp'; import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import PrevNext from 'ui/shared/PrevNext'; import PrevNext from 'ui/shared/PrevNext';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import ZkSyncL2TxnBatchStatus from 'ui/shared/statusTag/ZkSyncL2TxnBatchStatus'; import ZkSyncL2TxnBatchStatus from 'ui/shared/statusTag/ZkSyncL2TxnBatchStatus';
......
...@@ -9,7 +9,7 @@ import config from 'configs/app'; ...@@ -9,7 +9,7 @@ import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import ZkSyncL2TxnBatchStatus from 'ui/shared/statusTag/ZkSyncL2TxnBatchStatus'; import ZkSyncL2TxnBatchStatus from 'ui/shared/statusTag/ZkSyncL2TxnBatchStatus';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
...@@ -11,7 +11,7 @@ import getValueWithUnit from 'lib/getValueWithUnit'; ...@@ -11,7 +11,7 @@ import getValueWithUnit from 'lib/getValueWithUnit';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import BlobEntity from 'ui/shared/entities/blob/BlobEntity'; import BlobEntity from 'ui/shared/entities/blob/BlobEntity';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import TxFeeStability from 'ui/shared/tx/TxFeeStability'; import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
......
...@@ -8,7 +8,7 @@ import dayjs from 'lib/date/dayjs'; ...@@ -8,7 +8,7 @@ import dayjs from 'lib/date/dayjs';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
...@@ -8,7 +8,7 @@ import dayjs from 'lib/date/dayjs'; ...@@ -8,7 +8,7 @@ import dayjs from 'lib/date/dayjs';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
......
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