diff --git a/configs/app/features/multichainButton.ts b/configs/app/features/multichainButton.ts index 923a16bd27..228064628c 100644 --- a/configs/app/features/multichainButton.ts +++ b/configs/app/features/multichainButton.ts @@ -13,12 +13,15 @@ const config: Feature<{ providers: Array }> = (( return Object.freeze({ title, isEnabled: true, - providers: value.map((provider) => ({ - name: provider.name, - logoUrl: provider.logo, - urlTemplate: provider.url_template, - dappId: marketplace.isEnabled ? provider.dapp_id : undefined, - })), + providers: value + .map((provider) => ({ + name: provider.name, + logoUrl: provider.logo, + urlTemplate: provider.url_template, + dappId: marketplace.isEnabled ? provider.dapp_id : undefined, + promo: provider.promo, + })) + .sort((_, b) => (b.promo ? 1 : -1)), }); } diff --git a/configs/envs/.env.eth b/configs/envs/.env.eth index 77d36d6dad..00093c7b54 100644 --- a/configs/envs/.env.eth +++ b/configs/envs/.env.eth @@ -48,7 +48,7 @@ NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKj NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true NEXT_PUBLIC_MIXPANEL_CONFIG_OVERRIDES={"record_sessions_percent": 0.5,"record_heatmap_data": 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'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}] +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'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'},{'name': 'blockscout', 'url_template': 'https://superchain.blockscout.com/address/{address}', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/multichain-balance/blockscout.svg', 'promo': true}] NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG={"img_url": {"small": "https://blockscout-merits-images.s3.us-east-1.amazonaws.com/banners/sidemenu-banner-small.png", "large": "https://blockscout-merits-images.s3.us-east-1.amazonaws.com/banners/sidemenu-banner-big.png"}, "link_url": "https://www.blockscout.com/?utm_source=blockscout&utm_medium=side-menu-banner"} diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh index 50355f9e6e..d30c68eb1a 100755 --- a/deploy/scripts/entrypoint.sh +++ b/deploy/scripts/entrypoint.sh @@ -69,19 +69,27 @@ node --no-warnings ./og_image_generator.js ./make_envs_script.sh # Generate multichain config -node ./deploy/tools/multichain-config-generator/dist/index.js +node --no-warnings ./deploy/tools/multichain-config-generator/dist/index.js +if [ $? -ne 0 ]; then + echo "👎 Unable to generate multichain config." + exit 1 +fi # Generate essential dapps chains config -node ./deploy/tools/essential-dapps-chains-config-generator/dist/index.js +node --no-warnings ./deploy/tools/essential-dapps-chains-config-generator/dist/index.js +if [ $? -ne 0 ]; then + echo "👎 Unable to generate essential dapps chains config." + exit 1 +fi # Generate sitemap.xml and robots.txt files ./sitemap_generator.sh # Generate llms.txt file -node ./deploy/tools/llms-txt-generator/dist/index.js +node --no-warnings ./deploy/tools/llms-txt-generator/dist/index.js # Print list of enabled features -node ./feature-reporter.js +node --no-warnings ./feature-reporter.js echo "Starting Next.js application" exec "$@" diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index b852db51ab..031ff784f9 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -35,6 +35,7 @@ const multichainProviderConfigSchema: yup.ObjectSchema url_template: yup.string().required(), logo: yup.string().required(), dapp_id: yup.string(), + promo: yup.boolean(), }); const schema = yup diff --git a/deploy/tools/essential-dapps-chains-config-generator/index.ts b/deploy/tools/essential-dapps-chains-config-generator/index.ts index 7dcc74fe43..547a750e04 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/index.ts +++ b/deploy/tools/essential-dapps-chains-config-generator/index.ts @@ -17,23 +17,39 @@ const currentFilePath = fileURLToPath(import.meta.url); const currentDir = dirname(currentFilePath); async function getChainscoutInfo(externalChainIds: Array, currentChainId: string | undefined) { - const response = await fetch('https://chains.blockscout.com/api/chains'); - if (!response.ok) { - throw new Error(`Failed to fetch chains info from Chainscout API`); - } - const chainsInfo = await response.json() as Record; + + const controller = new AbortController(); + const timeout = setTimeout(() => { + controller.abort(`Request to Chainscout API timed out`); + }, 10_000); - return { - externals: externalChainIds.map((chainId) => ({ - id: chainId, - explorerUrl: chainsInfo[chainId]?.explorers[0]?.url, - logoUrl: chainsInfo[chainId]?.logo, - })), - current: currentChainId ? { - id: currentChainId, - explorerUrl: chainsInfo[currentChainId]?.explorers[0]?.url, - logoUrl: chainsInfo[currentChainId]?.logo, - } : undefined, + try { + const response = await fetch( + `https://chains.blockscout.com/api/chains?chain_ids=${ [currentChainId, ...externalChainIds].filter(Boolean).join(',') }`, + { signal: controller.signal } + ); + if (!response.ok) { + throw new Error(`Failed to fetch chains info from Chainscout API`); + } + const chainsInfo = await response.json() as Record; + + return { + externals: externalChainIds.map((chainId) => ({ + id: chainId, + explorerUrl: chainsInfo[chainId]?.explorers[0]?.url, + logoUrl: chainsInfo[chainId]?.logo, + })), + current: currentChainId ? { + id: currentChainId, + explorerUrl: chainsInfo[currentChainId]?.explorers[0]?.url, + logoUrl: chainsInfo[currentChainId]?.logo, + } : undefined, + } + + } catch (error) { + throw error; + } finally { + clearTimeout(timeout); } } diff --git a/deploy/tools/essential-dapps-chains-config-generator/worker.ts b/deploy/tools/essential-dapps-chains-config-generator/worker.ts index 449b390339..f73223cbde 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/worker.ts +++ b/deploy/tools/essential-dapps-chains-config-generator/worker.ts @@ -10,12 +10,23 @@ interface ChainConfig { } async function fetchChainConfig(url: string): Promise { - const response = await fetch(`${ url }/node-api/config`); - if (!response.ok) { - throw new Error(`Failed to fetch config from ${ url }: ${ response.statusText }`); + const controller = new AbortController(); + const timeout = setTimeout(() => { + controller.abort(`Request to ${ url } timed out`); + }, 5_000); + + try { + const response = await fetch(`${ url }/node-api/config`, { signal: controller.signal }); + if (!response.ok) { + throw new Error(`Failed to fetch config from ${ url }: ${ response.statusText }`); + } + const config = await response.json(); + return config as ChainConfig; + } catch (error) { + throw error; + } finally { + clearTimeout(timeout); } - const config = await response.json(); - return config as ChainConfig; } async function computeConfig() { diff --git a/deploy/tools/multichain-config-generator/index.ts b/deploy/tools/multichain-config-generator/index.ts index 1fbe0d4600..a7fbba2289 100644 --- a/deploy/tools/multichain-config-generator/index.ts +++ b/deploy/tools/multichain-config-generator/index.ts @@ -24,16 +24,27 @@ function getSlug(chainName: string) { } async function getChainscoutInfo(chainIds: Array) { - const response = await fetch('https://chains.blockscout.com/api/chains'); - if (!response.ok) { - throw new Error(`Failed to fetch chains info from Chainscout API`); - } - const chainsInfo = await response.json() as Record; + const controller = new AbortController(); + const timeout = setTimeout(() => { + controller.abort(`Request to Chainscout API timed out`); + }, 10_000); - return chainIds.map((chainId) => ({ - id: chainId, - logoUrl: chainsInfo[chainId]?.logo, - })) + try { + const response = await fetch(`https://chains.blockscout.com/api/chains?chain_ids=${ chainIds.join(',') }`, { signal: controller.signal }); + if (!response.ok) { + throw new Error(`Failed to fetch chains info from Chainscout API`); + } + const chainsInfo = await response.json() as Record; + + return chainIds.map((chainId) => ({ + id: chainId, + logoUrl: chainsInfo[chainId]?.logo, + })) + } catch (error) { + throw error; + } finally { + clearTimeout(timeout); + } } async function computeChainConfig(url: string): Promise { diff --git a/deploy/tools/multichain-config-generator/worker.ts b/deploy/tools/multichain-config-generator/worker.ts index 449b390339..f73223cbde 100644 --- a/deploy/tools/multichain-config-generator/worker.ts +++ b/deploy/tools/multichain-config-generator/worker.ts @@ -10,12 +10,23 @@ interface ChainConfig { } async function fetchChainConfig(url: string): Promise { - const response = await fetch(`${ url }/node-api/config`); - if (!response.ok) { - throw new Error(`Failed to fetch config from ${ url }: ${ response.statusText }`); + const controller = new AbortController(); + const timeout = setTimeout(() => { + controller.abort(`Request to ${ url } timed out`); + }, 5_000); + + try { + const response = await fetch(`${ url }/node-api/config`, { signal: controller.signal }); + if (!response.ok) { + throw new Error(`Failed to fetch config from ${ url }: ${ response.statusText }`); + } + const config = await response.json(); + return config as ChainConfig; + } catch (error) { + throw error; + } finally { + clearTimeout(timeout); } - const config = await response.json(); - return config as ChainConfig; } async function computeConfig() { diff --git a/docs/ENVS.md b/docs/ENVS.md index 17bdca972f..b7995c763e 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -102,6 +102,8 @@ All json-like values should be single-quoted. If it contains a hash (`#`) or a d *Note!* The `NEXT_PUBLIC_NETWORK_CURRENCY` variables represent the blockchain's native token used for paying transaction fees. `NEXT_PUBLIC_NETWORK_SECONDARY_COIN` variables refer to tokens like protocol-specific tokens (e.g., OP token on Optimism chain) or governance tokens (e.g., GNO on Gnosis chain). +Also, be aware that if you customize the name of the currency or any of its denominations (wei or gwei) while running Stats microservices, you may want to change those names in the indicators and charts returned by the microservice. To do this, pass the appropriate values to the Stats microservice environment variables, such as `STATS_CHARTS__LINE_CHARTS____UNITS` and `STATS_CHARTS__LINE_CHARTS____DESCRIPTION`. For the Average Gas Price chart, the `` will be `average_gas_price`. Please refer to the [microservice documentation](https://github.com/blockscout/blockscout-rs/tree/main/stats#charts) for the complete list of these variables. + | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | Required | - | `Gnosis Chain` | v1.0.x+ | @@ -913,6 +915,7 @@ If the feature is enabled, a Multichain balance button will be displayed on the | 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` | +| promo | `boolean` | Make the provider stand out by placing their logo prominently at the first place in the section and in the page subheader. | - | - | `true` |   diff --git a/mocks/metadata/appActionButton.ts b/mocks/metadata/appActionButton.ts index 47938638cd..f8d152deaa 100644 --- a/mocks/metadata/appActionButton.ts +++ b/mocks/metadata/appActionButton.ts @@ -9,12 +9,12 @@ const bgColor = '#FF007A'; export const buttonWithoutStyles: AddressMetadataTagApi['meta'] = { appID, - appMarketplaceURL, appLogoURL, appActionButtonText, }; export const linkWithoutStyles: AddressMetadataTagApi['meta'] = { + appID, appMarketplaceURL, appLogoURL, appActionButtonText, @@ -22,7 +22,6 @@ export const linkWithoutStyles: AddressMetadataTagApi['meta'] = { export const buttonWithStyles: AddressMetadataTagApi['meta'] = { appID, - appMarketplaceURL, appLogoURL, appActionButtonText, textColor, diff --git a/types/client/multichainProviderConfig.ts b/types/client/multichainProviderConfig.ts index 8d4a695c69..06ddb44b8b 100644 --- a/types/client/multichainProviderConfig.ts +++ b/types/client/multichainProviderConfig.ts @@ -3,6 +3,7 @@ export type MultichainProviderConfig = { dapp_id?: string; url_template: string; logo: string; + promo?: boolean; }; export type MultichainProviderConfigParsed = { @@ -10,4 +11,5 @@ export type MultichainProviderConfigParsed = { logoUrl: string; urlTemplate: string; dappId?: string; + promo?: boolean; }; diff --git a/ui/address/AddressMultichainInfoButton.tsx b/ui/address/AddressMultichainInfoButton.tsx new file mode 100644 index 0000000000..2adbfe39a0 --- /dev/null +++ b/ui/address/AddressMultichainInfoButton.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import type { Address } from 'types/api/address'; + +import config from 'configs/app'; +import type { LinkProps } from 'toolkit/chakra/link'; +import { Link } from 'toolkit/chakra/link'; + +const feature = config.features.multichainButton; + +interface Props extends LinkProps { + addressData?: Address; +} + +const AddressMultichainInfoButton = ({ addressData, ...rest }: Props) => { + if (!feature.isEnabled) { + return null; + } + + const promotedProvider = feature.providers.find((provider) => provider.promo); + + if (!promotedProvider) { + return null; + } + + if (!addressData || (addressData.is_contract && addressData.proxy_type !== 'eip7702')) { + return null; + } + + const url = (() => { + try { + const url = new URL(promotedProvider.urlTemplate.replace('{address}', addressData.hash)); + url.searchParams.append('utm_source', 'blockscout'); + url.searchParams.append('utm_medium', 'address'); + return url.toString(); + } catch (error) {} + return null; + })(); + + if (!url) { + return null; + } + + return ( + + Multichain info + + ); +}; + +export default React.memo(AddressMultichainInfoButton); diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png index 130351c71b..dc2bcdbef0 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png index f7feb2c03b..878accc68f 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png index acef8bc54b..87d4bb6821 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png differ diff --git a/ui/address/details/AddressMultichainButton.module.css b/ui/address/details/AddressMultichainButton.module.css new file mode 100644 index 0000000000..77b15a1c6c --- /dev/null +++ b/ui/address/details/AddressMultichainButton.module.css @@ -0,0 +1,119 @@ +/* Animation source - https://codepen.io/ARS/pen/vEwEPP */ +.promo { + position: relative; + z-index: 1; + + &::after { + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + margin: auto; + z-index: -1; + border-radius: 6px; + + content: ""; + background: linear-gradient(120deg, #e15eff, #fffb22, #e15eff); + background-size: 400% 400%; + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 4px, + 20px 4px, + 20px 20px, + 4px 20px, + 4px 100%, + 100% 100%, + 100% 0%, + 0% 0% + ); + animation: gradient 3s ease-in-out 0.5s infinite, border 5s ease-in-out; + } +} + +@keyframes gradient { + 0% { + background-position: 14% 0%; + } + 50% { + background-position: 87% 100%; + } + 100% { + background-position: 14% 0%; + } +} + +@keyframes border { + 0% { + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 4px 100%, + 0% 100% + ); + } + 5% { + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 4px, + 4px 4px, + 4px 4px, + 4px 4px, + 4px 4px, + 4px 4px, + 4px 0%, + 0% 0% + ); + } + 10% { + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 4px, + 20px 4px, + 20px 4px, + 20px 4px, + 20px 4px, + 20px 4px, + 100% 0%, + 0% 0% + ); + } + 15% { + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 4px, + 20px 4px, + 20px 20px, + 20px 20px, + 20px 100%, + 100% 100%, + 100% 0%, + 0% 0% + ); + } + 20%, + 100% { + clip-path: polygon( + 0% 100%, + 4px 100%, + 4px 4px, + 20px 4px, + 20px 20px, + 4px 20px, + 4px 100%, + 100% 100%, + 100% 0%, + 0% 0% + ); + } +} \ No newline at end of file diff --git a/ui/address/details/AddressMultichainButton.tsx b/ui/address/details/AddressMultichainButton.tsx index 5c48ed8ebd..f8afeb67fc 100644 --- a/ui/address/details/AddressMultichainButton.tsx +++ b/ui/address/details/AddressMultichainButton.tsx @@ -8,30 +8,36 @@ import { route } from 'nextjs-routes'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Tooltip } from 'toolkit/chakra/tooltip'; +import TextSeparator from 'ui/shared/TextSeparator'; + +import styles from './AddressMultichainButton.module.css'; const TEMPLATE_ADDRESS = '{address}'; type Props = { - hasSingleProvider: boolean; item: MultichainProviderConfigParsed; addressHash: string; onClick?: () => void; + isFirst: boolean; + isLast: boolean; }; -const AddressMultichainButton = ({ item, addressHash, onClick, hasSingleProvider }: Props) => { +const AddressMultichainButton = ({ item, addressHash, onClick, isFirst, isLast }: Props) => { + + const isOnlyOne = isFirst && isLast; const buttonIcon = ( { ); - const buttonContent = hasSingleProvider ? ( + const buttonContent = isOnlyOne ? ( <> { buttonIcon } { capitalize(item.name) } @@ -49,17 +55,21 @@ const AddressMultichainButton = ({ item, addressHash, onClick, hasSingleProvider const isExternal = typeof dappId !== 'string'; return ( - - { buttonContent } - + <> + + { buttonContent } + + { item.promo && isFirst && !isLast && } + ); } catch (error) {} diff --git a/ui/address/details/AddressNetWorth.pw.tsx b/ui/address/details/AddressNetWorth.pw.tsx index ace6b0ee74..65505cf379 100644 --- a/ui/address/details/AddressNetWorth.pw.tsx +++ b/ui/address/details/AddressNetWorth.pw.tsx @@ -9,6 +9,7 @@ import AddressNetWorth from './AddressNetWorth'; const ADDRESS_HASH = addressMock.hash; const ICON_URL = 'https://localhost:3000/my-icon.png'; +const ICON_URL_PROMO = 'https://localhost:3000/my-icon-promo.png'; test.beforeEach(async({ mockApiResponse }) => { await mockApiResponse('general:address_tokens', tokensMock.erc20List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } }); @@ -47,14 +48,16 @@ test('with single multichain button external', async({ render, mockEnvs, mockAss await expect(component).toHaveScreenshot(); }); -test('with two multichain button external', async({ render, mockEnvs, mockAssetResponse }) => { +test('with two multichain button and promo', async({ render, mockEnvs, mockAssetResponse }) => { await mockEnvs([ [ 'NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG', `[ {"name": "duck", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL }"}, - {"name": "duck2", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL }"} + {"name": "duck2", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL }"}, + {"name": "duck3", "url_template": "https://duck.url/{address}", "logo": "${ ICON_URL_PROMO }", "promo": true} ]` ], ]); await mockAssetResponse(ICON_URL, './playwright/mocks/image_svg.svg'); + await mockAssetResponse(ICON_URL_PROMO, './playwright/mocks/image_s.jpg'); const component = await render(); diff --git a/ui/address/details/AddressNetWorth.tsx b/ui/address/details/AddressNetWorth.tsx index 0303f8f891..e72acb81d0 100644 --- a/ui/address/details/AddressNetWorth.tsx +++ b/ui/address/details/AddressNetWorth.tsx @@ -1,4 +1,4 @@ -import { Text, Flex } from '@chakra-ui/react'; +import { Text, HStack } from '@chakra-ui/react'; import React from 'react'; import type { Address } from 'types/api/address'; @@ -47,24 +47,26 @@ const AddressNetWorth = ({ addressData, isLoading, addressHash }: Props) => { if (multichainFeature.isEnabled && (!addressData?.is_contract || isEip7702)) { const { providers } = multichainFeature; - const hasSingleProvider = providers.length === 1; multichainItems = ( <> - + Multichain - { providers.map((item) => ( - - )) - } - + + { providers.map((item, index) => ( + + )) + } + + ); } diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png new file mode 100644 index 0000000000..6d86db7e93 Binary files /dev/null and b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-external-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-external-1.png deleted file mode 100644 index ed16450dc1..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-external-1.png and /dev/null differ diff --git a/ui/address/tokens/ERC20TokensTableItem.tsx b/ui/address/tokens/ERC20TokensTableItem.tsx index b1eca4666b..d4446ab4d7 100644 --- a/ui/address/tokens/ERC20TokensTableItem.tsx +++ b/ui/address/tokens/ERC20TokensTableItem.tsx @@ -41,7 +41,7 @@ const ERC20TokensTableItem = ({ }, [ chainValues ]); return ( - + { isLoading={ isLoading } mode="compact" /> - - { !config.UI.views.tx.hiddenFields?.value && ( - - Value - { getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether } - - ) } - { !config.UI.views.tx.hiddenFields?.tx_fee && ( - - Fee - - - ) } - + { !(config.UI.views.tx.hiddenFields?.value && config.UI.views.tx.hiddenFields?.tx_fee) ? ( + + { !config.UI.views.tx.hiddenFields?.value && ( + + Value + { getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether } + + ) } + { !config.UI.views.tx.hiddenFields?.tx_fee && ( + + Fee + + + ) } + + ) : null } ); }; diff --git a/ui/home/LatestTxsItemMobile.tsx b/ui/home/LatestTxsItemMobile.tsx index 71535a8bc0..180c5eeb51 100644 --- a/ui/home/LatestTxsItemMobile.tsx +++ b/ui/home/LatestTxsItemMobile.tsx @@ -3,6 +3,7 @@ import { Flex, HStack, Text, + VStack, } from '@chakra-ui/react'; import React from 'react'; @@ -73,20 +74,23 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { to={ dataTo } isLoading={ isLoading } fontWeight="500" - mb={ 3 } /> - { !config.UI.views.tx.hiddenFields?.value && ( - - Value - { getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether } - - ) } - { !config.UI.views.tx.hiddenFields?.tx_fee && ( - - Fee - - - ) } + { !(config.UI.views.tx.hiddenFields?.value && config.UI.views.tx.hiddenFields?.tx_fee) ? ( + + { !config.UI.views.tx.hiddenFields?.value && ( + + Value + { getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether } + + ) } + { !config.UI.views.tx.hiddenFields?.tx_fee && ( + + Fee + + + ) } + + ) : null } ); }; diff --git a/ui/optimismSuperchain/address/__screenshots__/OpSuperchainAddressTxs.pw.tsx_default_local-txs-base-view-1.png b/ui/optimismSuperchain/address/__screenshots__/OpSuperchainAddressTxs.pw.tsx_default_local-txs-base-view-1.png index fd9a94ee38..606f5b5a46 100644 Binary files a/ui/optimismSuperchain/address/__screenshots__/OpSuperchainAddressTxs.pw.tsx_default_local-txs-base-view-1.png and b/ui/optimismSuperchain/address/__screenshots__/OpSuperchainAddressTxs.pw.tsx_default_local-txs-base-view-1.png differ diff --git a/ui/pages/Address.tsx b/ui/pages/Address.tsx index 6aac2b8ac6..f0a1c9a2c4 100644 --- a/ui/pages/Address.tsx +++ b/ui/pages/Address.tsx @@ -34,6 +34,7 @@ import AddressEpochRewards from 'ui/address/AddressEpochRewards'; import AddressInternalTxs from 'ui/address/AddressInternalTxs'; import AddressLogs from 'ui/address/AddressLogs'; import AddressMud from 'ui/address/AddressMud'; +import AddressMultichainInfoButton from 'ui/address/AddressMultichainInfoButton'; import AddressTokens from 'ui/address/AddressTokens'; import AddressTokenTransfers from 'ui/address/AddressTokenTransfers'; import AddressTxs from 'ui/address/AddressTxs'; @@ -449,6 +450,7 @@ const AddressPageContent = () => { + { !isLoading && addressQuery.data?.is_contract && addressQuery.data?.is_verified && config.UI.views.address.solidityscanEnabled && } { !isLoading && nameServicesFeature.isEnabled && nameServicesFeature.ens.isEnabled && diff --git a/ui/shared/AppActionButton/AppActionButton.tsx b/ui/shared/AppActionButton/AppActionButton.tsx index fb1025965d..74701dc972 100644 --- a/ui/shared/AppActionButton/AppActionButton.tsx +++ b/ui/shared/AppActionButton/AppActionButton.tsx @@ -29,7 +29,7 @@ const AppActionButton = ({ data, className, txHash, source }: Props) => { } }, [ source, appID, actionURL ]); - if ((!appID && !appMarketplaceURL) || (!appActionButtonText && !appLogoURL)) { + if ((!appID && !actionURL) || (!appActionButtonText && !appLogoURL)) { return null; } @@ -50,13 +50,11 @@ const AppActionButton = ({ data, className, txHash, source }: Props) => { ); - const isExternal = !appID; - return ( @@ -70,11 +78,11 @@ const TxsTable = ({ Type - Method + Method { showBlockInfo && ( onSortToggle ? ( ) : ( - Block + Block ) ) } - From/To + From/To { !config.UI.views.tx.hiddenFields?.value && ( onSortToggle ? ( ) : ( - Value + Value ) ) } { !config.UI.views.tx.hiddenFields?.tx_fee && ( onSortToggle ? ( ) : ( - Fee + Fee ) ) } diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index 2dd447f46b..2fc1e5ee89 100644 Binary files a/ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png b/ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png index d6d4acbc54..9f3708544c 100644 Binary files a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png and b/ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png b/ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png index 6dca2acf89..8996c3726c 100644 Binary files a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png and b/ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png differ