From 2f609050a3afb03a44a8ff631b7edd5c4bd11717 Mon Sep 17 00:00:00 2001 From: Salim Toubal Date: Mon, 3 Jun 2024 11:56:36 +0200 Subject: [PATCH] fix: deprecating more networks --- app/scripts/controllers/preferences.js | 1 - app/scripts/controllers/preferences.test.js | 2 - app/scripts/migrations/119.test.ts | 194 ++++++++++++++++++ app/scripts/migrations/119.ts | 81 ++++++++ app/scripts/migrations/index.js | 1 + shared/constants/network.ts | 7 - ui/helpers/utils/i18n-helper.tsx | 3 - .../templates/add-ethereum-chain.js | 22 +- .../templates/add-ethereum-chain.test.js | 49 ++++- .../privacy-settings/privacy-settings.js | 1 + .../privacy-settings/privacy-settings.test.js | 2 + .../networks-list-item/networks-list-item.js | 4 +- .../networks-tab/networks-tab.constants.js | 12 -- .../__snapshots__/security-tab.test.js.snap | 87 -------- ui/selectors/selectors.js | 13 -- ui/selectors/selectors.test.js | 4 +- 16 files changed, 351 insertions(+), 132 deletions(-) create mode 100644 app/scripts/migrations/119.test.ts create mode 100644 app/scripts/migrations/119.ts diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a8b6b7f1a755..0931bc18bc22 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -15,7 +15,6 @@ const mainNetworks = { const testNetworks = { [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.SEPOLIA]: true, - [CHAIN_IDS.LINEA_GOERLI]: true, [CHAIN_IDS.LINEA_SEPOLIA]: true, }; diff --git a/app/scripts/controllers/preferences.test.js b/app/scripts/controllers/preferences.test.js index be677cdd7c42..6425139c5049 100644 --- a/app/scripts/controllers/preferences.test.js +++ b/app/scripts/controllers/preferences.test.js @@ -372,7 +372,6 @@ describe('preferences controller', () => { [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1]].chainId]: true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.SEPOLIA]: true, - [CHAIN_IDS.LINEA_GOERLI]: true, [CHAIN_IDS.LINEA_SEPOLIA]: true, }); }); @@ -390,7 +389,6 @@ describe('preferences controller', () => { [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1]].chainId]: true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.SEPOLIA]: true, - [CHAIN_IDS.LINEA_GOERLI]: true, [CHAIN_IDS.LINEA_SEPOLIA]: true, }); }); diff --git a/app/scripts/migrations/119.test.ts b/app/scripts/migrations/119.test.ts new file mode 100644 index 000000000000..e4cc8751297d --- /dev/null +++ b/app/scripts/migrations/119.test.ts @@ -0,0 +1,194 @@ +import { NetworkType } from '@metamask/controller-utils'; +import { + CHAIN_IDS, + CHAIN_ID_TO_RPC_URL_MAP, + LINEA_SEPOLIA_DISPLAY_NAME, + NETWORK_TYPES, + TEST_NETWORK_TICKER_MAP, +} from '../../../shared/constants/network'; +import { migrate, version } from './119'; + +const oldVersion = 118; + +const ethereumProviderConfig = { + chainId: '0x1', + rpcPrefs: { + blockExplorerUrl: 'https://etherscan.io', + }, + ticker: 'ETH', + type: 'mainnet', +}; + +const ethereumNetworksMetadata = { + mainnet: { + EIPS: { + '1559': true, + }, + status: 'available', + }, +}; +const ethereumOldState = { + CurrencyController: { + currencyRates: { + ETH: { + conversionDate: 1708532473.416, + conversionRate: 2918.02, + usdConversionRate: 2918.02, + }, + GoerliETH: { + conversionDate: 1708532466.732, + conversionRate: 2918.02, + usdConversionRate: 2918.02, + }, + }, + currentCurrency: 'usd', + }, + NetworkController: { + networkConfigurations: {}, + networksMetadata: ethereumNetworksMetadata, + providerConfig: ethereumProviderConfig, + selectedNetworkClientId: 'mainnet', + }, +}; + +const lineaGoerliState = { + NetworkController: { + networkConfigurations: {}, + networksMetadata: { + 'linea-goerli': { + EIPS: { + '1559': true, + }, + status: 'available', + }, + }, + providerConfig: { + chainId: CHAIN_IDS.LINEA_GOERLI, + rpcPrefs: {}, + ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_GOERLI], + type: NETWORK_TYPES.LINEA_GOERLI, + }, + selectedNetworkClientId: NETWORK_TYPES.LINEA_GOERLI, + }, +}; + +describe('migration #119', () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('does nothing if no preferences state', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: oldState, + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('Should return state if chainId is not linea-goerli', async () => { + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: ethereumOldState, + }); + + expect(transformedState.data).toEqual(ethereumOldState); + }); + + it('Should return state if there is no NetworkController in state', async () => { + const { NetworkController, ...state } = ethereumOldState; + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: state, + }); + + expect(transformedState.data).toEqual(state); + }); + + it('Should return state if there is no provider in NetworkController', async () => { + const state = { + ...ethereumOldState, + NetworkController: {}, + }; + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: state, + }); + + expect(transformedState.data).toEqual(state); + }); + + it('Should return state if there is no chainId in provider in NetworkController', async () => { + const state = { + ...ethereumOldState, + NetworkController: { + providerConfig: {}, + }, + }; + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: state, + }); + + expect(transformedState.data).toEqual(state); + }); + + it('Should return state if chainId is not linea-goerli', async () => { + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: ethereumOldState, + }); + + expect(transformedState.data).toEqual(ethereumOldState); + }); + + it('Should update NetworkController to Linea Sepolia if chainId is on Linea Goerli', async () => { + const expectedNetworkControllerState = { + networkConfigurations: {}, + networksMetadata: { + 'linea-sepolia': { + EIPS: { + '1559': true, + }, + status: 'available', + }, + 'linea-goerli': { + EIPS: { + '1559': true, + }, + status: 'available', + }, + }, + providerConfig: { + type: NetworkType['linea-sepolia'], + rpcPrefs: {}, + chainId: CHAIN_IDS.LINEA_SEPOLIA, + nickname: LINEA_SEPOLIA_DISPLAY_NAME, + rpcUrl: CHAIN_ID_TO_RPC_URL_MAP[CHAIN_IDS.LINEA_SEPOLIA], + providerType: NETWORK_TYPES.LINEA_SEPOLIA, + ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_SEPOLIA], + id: NETWORK_TYPES.LINEA_SEPOLIA, + }, + selectedNetworkClientId: 'linea-sepolia', + }; + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: lineaGoerliState, + }); + + expect(transformedState.data).toEqual({ + NetworkController: expectedNetworkControllerState, + }); + }); +}); diff --git a/app/scripts/migrations/119.ts b/app/scripts/migrations/119.ts new file mode 100644 index 000000000000..36ad209df6ee --- /dev/null +++ b/app/scripts/migrations/119.ts @@ -0,0 +1,81 @@ +import { cloneDeep, isObject } from 'lodash'; +import { NetworkType } from '@metamask/controller-utils'; +import { hasProperty } from '@metamask/utils'; +import { NetworkStatus } from '@metamask/network-controller'; +import { + CHAIN_IDS, + CHAIN_ID_TO_RPC_URL_MAP, + NETWORK_TYPES, + TEST_NETWORK_TICKER_MAP, + LINEA_SEPOLIA_DISPLAY_NAME, +} from '../../../shared/constants/network'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 119; + +/** + * Migrates the user network to Linea Sepolia if the user is on Linea Goerli network. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function transformState(state: Record) { + const NetworkController = state?.NetworkController || {}; + const provider = NetworkController?.providerConfig || {}; + + if (provider?.chainId !== CHAIN_IDS.LINEA_GOERLI) { + return state; + } + const networkControllerState = state.NetworkController; + + if ( + hasProperty(state, 'NetworkController') && + isObject(state.NetworkController) && + hasProperty(state.NetworkController, 'providerConfig') && + isObject(state.NetworkController.providerConfig) && + hasProperty(state.NetworkController.providerConfig, 'chainId') && + state.NetworkController.providerConfig.chainId === CHAIN_IDS.LINEA_GOERLI + ) { + networkControllerState.providerConfig = { + type: NetworkType['linea-sepolia'], + rpcPrefs: {}, + chainId: CHAIN_IDS.LINEA_SEPOLIA, + nickname: LINEA_SEPOLIA_DISPLAY_NAME, + rpcUrl: CHAIN_ID_TO_RPC_URL_MAP[CHAIN_IDS.LINEA_SEPOLIA], + providerType: NETWORK_TYPES.LINEA_SEPOLIA, + ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_SEPOLIA], + id: NETWORK_TYPES.LINEA_SEPOLIA, + }; + networkControllerState.selectedNetworkClientId = + NETWORK_TYPES.LINEA_SEPOLIA; + networkControllerState.networksMetadata = { + ...networkControllerState.networksMetadata, + 'linea-sepolia': { + EIPS: { + '1559': true, + }, + status: NetworkStatus.Available, + }, + }; + } + return { + ...state, + NetworkController: networkControllerState, + }; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 9cd79ee18a35..bcd4fef72698 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -129,6 +129,7 @@ const migrations = [ require('./116'), require('./117'), require('./118'), + require('./119'), ]; export default migrations; diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 5a4410d4d70f..368d1a1610b6 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -446,14 +446,12 @@ export const NUMBERS_TOKEN_IMAGE_URL = './images/numbers-token.png'; export const INFURA_PROVIDER_TYPES = [ NETWORK_TYPES.MAINNET, NETWORK_TYPES.SEPOLIA, - NETWORK_TYPES.LINEA_GOERLI, NETWORK_TYPES.LINEA_SEPOLIA, NETWORK_TYPES.LINEA_MAINNET, ] as const; export const TEST_CHAINS = [ CHAIN_IDS.SEPOLIA, - NETWORK_TYPES.LINEA_GOERLI, CHAIN_IDS.LINEA_SEPOLIA, CHAIN_IDS.LOCALHOST, ]; @@ -491,11 +489,6 @@ export const BUILT_IN_NETWORKS = { ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.SEPOLIA], blockExplorerUrl: `https://${NETWORK_TYPES.SEPOLIA}.etherscan.io`, }, - [NETWORK_TYPES.LINEA_GOERLI]: { - chainId: CHAIN_IDS.LINEA_GOERLI, - ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_GOERLI], - blockExplorerUrl: 'https://goerli.lineascan.build', - }, [NETWORK_TYPES.LINEA_SEPOLIA]: { chainId: CHAIN_IDS.LINEA_SEPOLIA, ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_SEPOLIA], diff --git a/ui/helpers/utils/i18n-helper.tsx b/ui/helpers/utils/i18n-helper.tsx index b639a158b312..eb53c9b5afd6 100644 --- a/ui/helpers/utils/i18n-helper.tsx +++ b/ui/helpers/utils/i18n-helper.tsx @@ -47,9 +47,6 @@ export const getMessage = ( }; export function getNetworkLabelKey(network: string): string { - if (network === NETWORK_TYPES.LINEA_GOERLI) { - return 'lineaGoerli'; - } if (network === NETWORK_TYPES.LINEA_SEPOLIA) { return 'lineaSepolia'; } diff --git a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js index f4ee0f60893b..4b6e3d7a1480 100644 --- a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js +++ b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js @@ -1,6 +1,9 @@ import { ethErrors } from 'eth-rpc-errors'; import React from 'react'; -import { infuraProjectId } from '../../../../../shared/constants/network'; +import { + infuraProjectId, + DEPRECATED_NETWORKS, +} from '../../../../../shared/constants/network'; import { Severity, TypographyVariant, @@ -74,6 +77,20 @@ const MISMATCHED_CHAIN_RECOMMENDATION = { }, }; +const DEPRECATED_CHAIN_ALERT = { + id: 'DEPRECATED_CHAIN_ALERT', + severity: Severity.Warning, + content: { + element: 'span', + children: { + element: 'MetaMaskTranslation', + props: { + translationKey: 'deprecatedNetwork', + }, + }, + }, +}; + const MISMATCHED_NETWORK_NAME = { id: 'MISMATCHED_NETWORK_NAME', severity: Severity.Warning, @@ -172,6 +189,9 @@ async function getAlerts(pendingApproval, data) { ) { alerts.push(MISMATCHED_NETWORK_RPC); } + if (DEPRECATED_NETWORKS.includes(pendingApproval.requestData.chainId)) { + alerts.push(DEPRECATED_CHAIN_ALERT); + } } if (!data.matchedChain && data.useSafeChainsListValidation) { diff --git a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.test.js b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.test.js index 9079b2e80111..f260252387c9 100644 --- a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.test.js +++ b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.test.js @@ -1,13 +1,15 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { waitFor } from '@testing-library/react'; +import { waitFor, act } from '@testing-library/react'; import { NetworkStatus } from '@metamask/network-controller'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import Confirmation from '../confirmation'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import fetchWithCache from '../../../../../shared/lib/fetch-with-cache'; jest.mock('../../../../../shared/lib/fetch-with-cache'); @@ -80,6 +82,51 @@ describe('add-ethereum-chain confirmation', () => { }); }); + it('should show deprecation alert', async () => { + const testStore = { + metamask: { + ...mockBaseStore.metamask, + useSafeChainsListValidation: true, + pendingApprovals: { + [mockApprovalId]: { + ...mockApproval, + requestData: { + rpcUrl: 'https://rpcurl.test.chain', + rpcPrefs: { + blockExplorerUrl: 'https://blockexplorer.test.chain', + }, + chainName: 'Test chain', + ticker: 'TST', + chainId: CHAIN_IDS.LINEA_GOERLI, // mumbai chainId + nickname: 'Test chain', + }, + type: MESSAGE_TYPE.ADD_ETHEREUM_CHAIN, + }, + }, + }, + }; + + const store = configureMockStore(middleware)(testStore); + fetchWithCache.mockResolvedValue([ + { + name: 'Linea Goerli', + title: 'Linea Goerli Testnet', + shortName: 'linea-goerli', + chainId: 59140, + }, + ]); + + let result; + act(() => { + result = renderWithProvider(, store); + }); + const { getByText } = result; + + await waitFor(() => { + expect(getByText('This network is deprecated')).toBeInTheDocument(); + }); + }); + it('should convert RPC URL to lowercase', async () => { const testStore = { metamask: { diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js index 3041cec21839..21f3461972c8 100644 --- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js +++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js @@ -125,6 +125,7 @@ export default function PrivacySettings() { ); const handleSubmit = () => { + console.log('IM HERE ========>'); dispatch(toggleExternalServices(externalServicesOnboardingToggleState)); dispatch(setUsePhishDetect(usePhishingDetection)); dispatch(setUse4ByteResolution(turnOn4ByteResolution)); diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js index 28046a4d5375..84461097dc78 100644 --- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js +++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js @@ -120,7 +120,9 @@ describe('Privacy Settings Onboarding View', () => { fireEvent.click(toggles[1]); // setIncomingTransactionsPreferencesStub fireEvent.click(toggles[2]); // setIncomingTransactionsPreferencesStub (2) fireEvent.click(toggles[3]); // setIncomingTransactionsPreferencesStub (3) + fireEvent.click(toggles[4]); // setIncomingTransactionsPreferencesStub (4) fireEvent.click(toggles[5]); // setUsePhishDetectStub + fireEvent.click(toggles[6]); fireEvent.click(toggles[7]); // setUse4ByteResolutionStub fireEvent.click(toggles[8]); // setUseTokenDetectionStub fireEvent.click(toggles[9]); // setUseMultiAccountBalanceCheckerStub diff --git a/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js b/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js index b480730f796c..c23a1c6a4a3b 100644 --- a/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js +++ b/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js @@ -102,9 +102,7 @@ const NetworksListItem = ({ ) )} {network.isATestNetwork && - ![CHAIN_IDS.LINEA_GOERLI, CHAIN_IDS.LINEA_SEPOLIA].includes( - network.chainId, - ) && ( + ![CHAIN_IDS.LINEA_SEPOLIA].includes(network.chainId) && ( -
-
-
- Linea Goerli logo -
-
-

- Linea Goerli -

-

- - Lineascan.build - -

-
-
-
-