diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9ec138eac03..c16354f54e9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,8 @@ # Each line is a file pattern followed by one or more owners. * @MetaMask/mobile-devs -app/component-library/ @MetaMask/design-system-engineers -app/components/UI/Ramp/ @MetaMask/ramp -app/reducers/fiatOrders/ @MetaMask/ramp +app/component-library/ @MetaMask/design-system-engineers @MetaMask/mobile-platform +patches/ @MetaMask/mobile-platform +bitrise.yml @MetaMask/mobile-platform +app/components/UI/Ramp/ @MetaMask/ramp @MetaMask/mobile-platform +app/reducers/fiatOrders/ @MetaMask/ramp @MetaMask/mobile-platform diff --git a/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.test.tsx b/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.test.tsx index 45b0b9f28dc..937e1d3f9c3 100644 --- a/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.test.tsx +++ b/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.test.tsx @@ -7,7 +7,7 @@ import { useSelector } from 'react-redux'; const mockNetworkInfo = { chainName: 'Test Chain', - chainId: '1', + chainId: '0xa', rpcUrl: 'http://test.com', ticker: 'TEST', blockExplorerUrl: 'http://explorer.test.com', @@ -68,4 +68,13 @@ describe('NetworkVerificationInfo', () => { getByText(strings('add_custom_network.unrecognized_chain_name')), ).toThrow('Unable to find an element with text'); }); + + it('should render chainId on decimal', () => { + (useSelector as jest.Mock).mockReturnValue(true); + const { getByText } = render( + , + ); + + expect(getByText('10')).toBeTruthy(); + }); }); diff --git a/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.tsx b/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.tsx index 31eb57ecfbc..7db6acaa005 100644 --- a/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.tsx +++ b/app/components/UI/NetworkVerificationInfo/NetworkVerificationInfo.tsx @@ -34,6 +34,7 @@ import { } from '../../../util/networks'; import { NetworkApprovalModalSelectorsIDs } from '../../../../e2e/selectors/Modals/NetworkApprovalModal.selectors'; import hideKeyFromUrl from '../../../util/hideKeyFromUrl'; +import { convertHexToDecimal } from '@metamask/controller-utils'; interface Alert { alertError: string; @@ -106,7 +107,9 @@ const NetworkVerificationInfo = ({ {strings('add_custom_network.chain_id')} - {customNetworkInformation.chainId} + + {convertHexToDecimal(customNetworkInformation.chainId)} + {strings('add_custom_network.network_url')} diff --git a/app/components/UI/NetworkVerificationInfo/__snapshots__/NetworkVerificationInfo.test.tsx.snap b/app/components/UI/NetworkVerificationInfo/__snapshots__/NetworkVerificationInfo.test.tsx.snap index 4591659646d..f892eee35fc 100644 --- a/app/components/UI/NetworkVerificationInfo/__snapshots__/NetworkVerificationInfo.test.tsx.snap +++ b/app/components/UI/NetworkVerificationInfo/__snapshots__/NetworkVerificationInfo.test.tsx.snap @@ -93,32 +93,33 @@ exports[`NetworkVerificationInfo renders correctly 1`] = ` - - T - + testID="network-avatar-image" + /> - 1 + 10 ({ useSelector: jest.fn(), })); -describe('useNativeTokenFiatAmount', () => { +describe('useIsOriginalNativeTokenSymbol', () => { afterEach(() => { jest.clearAllMocks(); }); diff --git a/app/core/RPCMethods/networkChecker.test.ts b/app/core/RPCMethods/networkChecker.test.ts index 4478678560c..f98223d5b12 100644 --- a/app/core/RPCMethods/networkChecker.test.ts +++ b/app/core/RPCMethods/networkChecker.test.ts @@ -212,4 +212,68 @@ describe('checkSafeNetwork', () => { ); expect(alerts).toEqual([]); }); + + it('should not return warning name/symbol if it match with popular network', async () => { + mockedAxios.get.mockImplementation(() => + Promise.resolve({ + data: [ + { + chainId: '10', + rpc: ['https://optimism-mainnet.infura.io/v3/1234'], + name: 'Optimism', + nativeCurrency: { + symbol: 'ETH', + decimals: 18, + }, + }, + ], + }), + ); + + const alerts = await checkSafeNetwork( + '10', + 'https://optimism-mainnet.infura.io/v3/1234', + 'Optimism', + 'ETH', + ); + expect(alerts).toEqual([]); + }); + it('should return warning name/symbol if it doesnt match with popular network and third part provider', async () => { + mockedAxios.get.mockImplementation(() => + Promise.resolve({ + data: [ + { + chainId: '10', + rpc: ['https://optimism-mainnet.infura.io/v3/1234'], + name: 'Optimism', + nativeCurrency: { + symbol: 'ETH', + decimals: 18, + }, + }, + ], + }), + ); + + const alerts = await checkSafeNetwork( + '10', + 'https://optimism-mainnet.infura.io/v3/1234', + 'Optimism test', + 'OP', + ); + expect(alerts).toEqual([ + { + alertError: + "It looks like this network's display name doesn't match its chain ID.", + alertSeverity: BannerAlertSeverity.Warning, + alertOrigin: 'chain_name', + }, + { + alertError: + "It looks like this network's symbol doesn't match this chain ID.", + alertSeverity: BannerAlertSeverity.Warning, + alertOrigin: 'chain_ticker', + }, + ]); + }); }); diff --git a/app/core/RPCMethods/networkChecker.util.ts b/app/core/RPCMethods/networkChecker.util.ts index 8342d0d210a..ad42c919533 100644 --- a/app/core/RPCMethods/networkChecker.util.ts +++ b/app/core/RPCMethods/networkChecker.util.ts @@ -2,13 +2,26 @@ import axios from 'axios'; import { BannerAlertSeverity } from '../../component-library/components/Banners/Banner'; import { strings } from '../../../locales/i18n'; import PopularList from '../../util/networks/customNetworks'; +import { toHex } from '@metamask/controller-utils'; -const findPopularNetwork = (rpcUrl: string) => +const findPopularNetwork = (rpcUrl: string, chainId: string) => PopularList.some((network) => { const { origin } = new URL(network.rpcUrl); - return origin === rpcUrl; + return origin === rpcUrl && network.chainId === chainId; }); +const findPopularNetworkName = (name: string, chainId: string) => + PopularList.some( + (network) => + network.nickname.toLowerCase() === name.toLowerCase() && + network.chainId === chainId, + ); + +const findPopularNetworkSymbol = (symbol: string, chainId: string) => + PopularList.some( + (network) => network.ticker === symbol && network.chainId === chainId, + ); + const checkSafeNetwork = async ( chainIdDecimal: string, rpcUrl: string, @@ -31,7 +44,7 @@ const checkSafeNetwork = async ( !matchedChain.rpc ?.map((rpc: string) => new URL(rpc).origin) .includes(origin) && - !findPopularNetwork(origin) + !findPopularNetwork(origin, toHex(chainIdDecimal)) ) { alerts.push({ alertError: strings('add_custom_network.invalid_rpc_url'), @@ -46,14 +59,20 @@ const checkSafeNetwork = async ( alertOrigin: 'decimals', }); } - if (matchedChain.name?.toLowerCase() !== nickname?.toLowerCase()) { + if ( + matchedChain.name?.toLowerCase() !== nickname?.toLowerCase() && + !findPopularNetworkName(nickname, toHex(chainIdDecimal)) + ) { alerts.push({ alertError: strings('add_custom_network.unrecognized_chain_name'), alertSeverity: BannerAlertSeverity.Warning, alertOrigin: 'chain_name', }); } - if (matchedChain.nativeCurrency?.symbol !== ticker) { + if ( + matchedChain.nativeCurrency?.symbol !== ticker && + !findPopularNetworkSymbol(ticker, toHex(chainIdDecimal)) + ) { alerts.push({ alertError: strings('add_custom_network.unrecognized_chain_ticker'), alertSeverity: BannerAlertSeverity.Warning,