From 2fc0d9378902fe7c3abf7393a443b82cff067f84 Mon Sep 17 00:00:00 2001 From: micaelae <100321200+micaelae@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:24:10 -0700 Subject: [PATCH] Disable Bridge button on unsupported networks (#18268) --- shared/constants/bridge.ts | 10 ++++ .../app/wallet-overview/eth-overview.js | 43 +++++++++++------ .../app/wallet-overview/eth-overview.test.js | 46 +++++++++++++++++-- ui/components/ui/icon-button/icon-button.scss | 1 + ui/selectors/selectors.js | 7 +++ ui/selectors/selectors.test.js | 10 ++++ 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 shared/constants/bridge.ts diff --git a/shared/constants/bridge.ts b/shared/constants/bridge.ts new file mode 100644 index 000000000..a70c0a62a --- /dev/null +++ b/shared/constants/bridge.ts @@ -0,0 +1,10 @@ +import { CHAIN_IDS } from './network'; + +export const ALLOWED_BRIDGE_CHAIN_IDS = [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.BSC, + CHAIN_IDS.POLYGON, + CHAIN_IDS.AVALANCHE, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.ARBITRUM, +]; diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index a02c8459d..12e69b59b 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -19,6 +19,7 @@ import { getCurrentKeyring, getSwapsDefaultToken, getIsSwapsChain, + getIsBridgeChain, getIsBuyableChain, getNativeCurrencyImage, getSelectedAccountCachedBalance, @@ -56,6 +57,7 @@ const EthOverview = ({ className }) => { const showFiat = useSelector(getShouldShowFiat); const balance = useSelector(getSelectedAccountCachedBalance); const isSwapsChain = useSelector(getIsSwapsChain); + const isBridgeChain = useSelector(getIsBridgeChain); const isBuyableChain = useSelector(getIsBuyableChain); const primaryTokenImage = useSelector(getNativeCurrencyImage); const defaultSwapsToken = useSelector(getSwapsDefaultToken); @@ -232,26 +234,41 @@ const EthOverview = ({ className }) => { /> } label={t('bridge')} onClick={() => { - const portfolioUrl = process.env.PORTFOLIO_URL; - const bridgeUrl = `${portfolioUrl}/bridge`; - global.platform.openTab({ - url: `${bridgeUrl}?metamaskEntry=ext`, - }); - trackEvent({ - category: EVENT.CATEGORIES.NAVIGATION, - event: EVENT_NAMES.BRIDGE_LINK_CLICKED, - properties: { - location: 'Home', - text: 'Bridge', - }, - }); + if (isBridgeChain) { + const portfolioUrl = process.env.PORTFOLIO_URL; + const bridgeUrl = `${portfolioUrl}/bridge`; + global.platform.openTab({ + url: `${bridgeUrl}?metamaskEntry=ext`, + }); + trackEvent({ + category: EVENT.CATEGORIES.NAVIGATION, + event: EVENT_NAMES.BRIDGE_LINK_CLICKED, + properties: { + location: 'Home', + text: 'Bridge', + }, + }); + } }} + tooltipRender={ + isBridgeChain + ? null + : (contents) => ( + + {contents} + + ) + } /> } diff --git a/ui/components/app/wallet-overview/eth-overview.test.js b/ui/components/app/wallet-overview/eth-overview.test.js index c73377199..3d4f459c1 100644 --- a/ui/components/app/wallet-overview/eth-overview.test.js +++ b/ui/components/app/wallet-overview/eth-overview.test.js @@ -145,13 +145,30 @@ describe('EthOverview', () => { expect(secondaryBalance).toHaveTextContent('0'); }); - it('should always show the Bridge button', () => { - const { queryByTestId } = renderWithProvider(, store); + it('should have the Bridge button enabled if chain id is part of supported chains', () => { + const mockedAvalancheStore = { + ...mockStore, + metamask: { + ...mockStore.metamask, + provider: { ...mockStore.metamask.provider, chainId: '0xa86a' }, + }, + }; + const mockedStore = configureMockStore([thunk])(mockedAvalancheStore); + + const { queryByTestId, queryByText } = renderWithProvider( + , + mockedStore, + ); const bridgeButton = queryByTestId(ETH_OVERVIEW_BRIDGE); expect(bridgeButton).toBeInTheDocument(); + expect(bridgeButton).toBeEnabled(); + expect(queryByText('Bridge').parentElement).not.toHaveAttribute( + 'data-original-title', + 'Unavailable on this network', + ); }); - it('should open the Bridge URI when clicking on Bridge button', async () => { + it('should open the Bridge URI when clicking on Bridge button on supported network', async () => { const { queryByTestId } = renderWithProvider(, store); const bridgeButton = queryByTestId(ETH_OVERVIEW_BRIDGE); @@ -169,6 +186,29 @@ describe('EthOverview', () => { ); }); + it('should have the Bridge button disabled if chain id is not part of supported chains', () => { + const mockedFantomStore = { + ...mockStore, + metamask: { + ...mockStore.metamask, + provider: { ...mockStore.metamask.provider, chainId: '0xfa' }, + }, + }; + const mockedStore = configureMockStore([thunk])(mockedFantomStore); + + const { queryByTestId, queryByText } = renderWithProvider( + , + mockedStore, + ); + const bridgeButton = queryByTestId(ETH_OVERVIEW_BRIDGE); + expect(bridgeButton).toBeInTheDocument(); + expect(bridgeButton).toBeDisabled(); + expect(queryByText('Bridge').parentElement).toHaveAttribute( + 'data-original-title', + 'Unavailable on this network', + ); + }); + it('should always show the Portfolio button', () => { const { queryByTestId } = renderWithProvider(, store); const portfolioButton = queryByTestId(ETH_OVERVIEW_PORTFOLIO); diff --git a/ui/components/ui/icon-button/icon-button.scss b/ui/components/ui/icon-button/icon-button.scss index 4ce70027c..8769d532c 100644 --- a/ui/components/ui/icon-button/icon-button.scss +++ b/ui/components/ui/icon-button/icon-button.scss @@ -22,6 +22,7 @@ border-radius: 18px; margin-top: 6px; margin-bottom: 5px; + margin-inline: auto; } &--disabled { diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index dea043e15..425107475 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -41,6 +41,8 @@ import { ALLOWED_DEV_SWAPS_CHAIN_IDS, } from '../../shared/constants/swaps'; +import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../shared/constants/bridge'; + import { shortenAddress, getAccountByAddress, @@ -750,6 +752,11 @@ export function getIsSwapsChain(state) { : ALLOWED_DEV_SWAPS_CHAIN_IDS.includes(chainId); } +export function getIsBridgeChain(state) { + const chainId = getCurrentChainId(state); + return ALLOWED_BRIDGE_CHAIN_IDS.includes(chainId); +} + export function getIsBuyableChain(state) { const chainId = getCurrentChainId(state); return Object.keys(BUYABLE_CHAINS_MAP).includes(chainId); diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index f3593f937..f1f7bfe5e 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -390,4 +390,14 @@ describe('Selectors', () => { const isDesktopEnabled = selectors.getIsDesktopEnabled(mockState); expect(isDesktopEnabled).toBeFalsy(); }); + + it('#getIsBridgeChain', () => { + mockState.metamask.provider.chainId = '0xa'; + const isOptimismSupported = selectors.getIsBridgeChain(mockState); + expect(isOptimismSupported).toBeTruthy(); + + mockState.metamask.provider.chainId = '0xfa'; + const isFantomSupported = selectors.getIsBridgeChain(mockState); + expect(isFantomSupported).toBeFalsy(); + }); });