From 9354d022e13021f86b68ab9069948e973a18b571 Mon Sep 17 00:00:00 2001 From: salimtb Date: Tue, 22 Oct 2024 13:50:12 +0200 Subject: [PATCH 1/3] feat: add new default networks --- ui/pages/onboarding-flow/welcome/welcome.js | 62 +++++++++++++++++++ .../onboarding-flow/welcome/welcome.test.js | 51 +++++++++++++++ ui/selectors/selectors.js | 4 ++ 3 files changed, 117 insertions(+) diff --git a/ui/pages/onboarding-flow/welcome/welcome.js b/ui/pages/onboarding-flow/welcome/welcome.js index 52825815be70..6b2377adf495 100644 --- a/ui/pages/onboarding-flow/welcome/welcome.js +++ b/ui/pages/onboarding-flow/welcome/welcome.js @@ -5,6 +5,7 @@ import { useHistory } from 'react-router-dom'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { Carousel } from 'react-responsive-carousel'; ///: END:ONLY_INCLUDE_IF +import { CHAIN_IDS } from '@metamask/transaction-controller'; import Mascot from '../../../components/ui/mascot'; import Button from '../../../components/ui/button'; import { Text } from '../../../components/component-library'; @@ -25,6 +26,8 @@ import { import { setFirstTimeFlowType, setTermsOfUseLastAgreed, + addNetwork, + updateNetworksList, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) setParticipateInMetaMetrics, ///: END:ONLY_INCLUDE_IF @@ -42,6 +45,8 @@ import { } from '../../../helpers/constants/routes'; import { getFirstTimeFlowType, getCurrentKeyring } from '../../../selectors'; import { FirstTimeFlowType } from '../../../../shared/constants/onboarding'; +import { FEATURED_RPCS } from '../../../../shared/constants/network'; +import { getCompletedOnboarding } from '../../../ducks/metamask/metamask'; export default function OnboardingWelcome() { const t = useI18nContext(); @@ -54,6 +59,8 @@ export default function OnboardingWelcome() { const [newAccountCreationInProgress, setNewAccountCreationInProgress] = useState(false); + const completedOnboarding = useSelector(getCompletedOnboarding); + // Don't allow users to come back to this screen after they // have already imported or created a wallet useEffect(() => { @@ -72,7 +79,62 @@ export default function OnboardingWelcome() { history, firstTimeFlowType, newAccountCreationInProgress, + dispatch, ]); + + useEffect(() => { + const addNetworks = async () => { + if (!completedOnboarding) { + // List of chainIds to add (as hex strings) + const chainIdsToAdd = [ + CHAIN_IDS.ARBITRUM, + CHAIN_IDS.BASE, + CHAIN_IDS.BSC, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.POLYGON, + ]; + + // Define the desired order based on `networkId` + const networkOrder = [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.ARBITRUM, + CHAIN_IDS.BASE, + CHAIN_IDS.BSC, + CHAIN_IDS.LINEA_MAINNET, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.POLYGON, + ]; + + // Filter the FEATURED_RPCS based on the chainIdsToAdd array + const selectedNetworks = FEATURED_RPCS.filter((network) => + chainIdsToAdd.includes(network.chainId), + ); + + for (const network of selectedNetworks) { + await dispatch( + addNetwork({ + chainId: network.chainId, + blockExplorerUrls: network.blockExplorerUrls, + defaultRpcEndpointIndex: network.defaultRpcEndpointIndex, + defaultBlockExplorerUrlIndex: + network.defaultBlockExplorerUrlIndex, + name: network.name, + nativeCurrency: network.nativeCurrency, + rpcEndpoints: network.rpcEndpoints, + }), + ); + console.log(`Successfully added network: ${network.name}`); + } + + await dispatch(updateNetworksList(networkOrder)); + } + }; + + addNetworks().catch((error) => { + console.error('Error adding networks:', error); + }); + }, [dispatch, completedOnboarding]); + const trackEvent = useContext(MetaMetricsContext); const onCreateClick = async () => { diff --git a/ui/pages/onboarding-flow/welcome/welcome.test.js b/ui/pages/onboarding-flow/welcome/welcome.test.js index c76f93a21f75..ba79f2e1e579 100644 --- a/ui/pages/onboarding-flow/welcome/welcome.test.js +++ b/ui/pages/onboarding-flow/welcome/welcome.test.js @@ -7,6 +7,8 @@ import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { setFirstTimeFlowType, setTermsOfUseLastAgreed, + addNetwork, + updateNetworksList, } from '../../../store/actions'; import { ONBOARDING_METAMETRICS, @@ -35,6 +37,8 @@ jest.mock('../../../store/actions.ts', () => ({ return type; }), ), + addNetwork: jest.fn(), + updateNetworksList: jest.fn(), })); jest.mock('react-router-dom', () => ({ @@ -122,5 +126,52 @@ describe('Onboarding Welcome Component', () => { expect(mockHistoryPush).toHaveBeenCalledWith(ONBOARDING_METAMETRICS); }); }); + + describe('useEffect for adding networks', () => { + it('should add networks when onboarding is incomplete', async () => { + // Render the component + renderWithProvider(, mockStore); + + await waitFor(() => { + // Verify addNetwork is called with Arbitrum (or other networks) + expect(addNetwork).toHaveBeenCalledWith({ + chainId: '0xa4b1', // Arbitrum's chain ID + blockExplorerUrls: ['https://explorer.arbitrum.io'], // Arbitrum block explorer + defaultRpcEndpointIndex: 0, + defaultBlockExplorerUrlIndex: 0, + name: 'Arbitrum One', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + url: 'https://arbitrum-mainnet.infura.io/v3/undefined', + type: 'custom', + }, + ], + }); + }); + }); + + it('should not add networks when onboarding is completed', async () => { + const mockCompletedOnboardingState = { + ...mockState, + metamask: { + ...mockState.metamask, + completedOnboarding: true, // Simulate completed onboarding + }, + }; + const mockStore2 = configureMockStore([thunk])( + mockCompletedOnboardingState, + ); + + renderWithProvider(, mockStore2); + + // Wait to ensure the effect runs (or doesn't in this case) + await waitFor(() => { + // Expect that neither addNetwork nor updateNetworksList was called + expect(addNetwork).not.toHaveBeenCalled(); + expect(updateNetworksList).not.toHaveBeenCalled(); + }); + }); + }); }); }); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 09c062012731..6914ce304f53 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1822,6 +1822,10 @@ export function getSortedAnnouncementsToShow(state) { * @returns {{networkId: string}[]} */ export function getOrderedNetworksList(state) { + console.log( + 'state.metamask.orderedNetworkList ----', + state.metamask.orderedNetworkList, + ); return state.metamask.orderedNetworkList; } From cc9eac5652f6142ac8f0756a44c46218a57a81da Mon Sep 17 00:00:00 2001 From: salimtb Date: Wed, 23 Oct 2024 11:53:53 +0200 Subject: [PATCH 2/3] fix: fix e2e tests --- privacy-snapshot.json | 1 + test/e2e/tests/network/multi-rpc.spec.ts | 58 ++++++++++++------------ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/privacy-snapshot.json b/privacy-snapshot.json index 41b04a9b5210..846674b77855 100644 --- a/privacy-snapshot.json +++ b/privacy-snapshot.json @@ -8,6 +8,7 @@ "api.web3modal.com", "app.ens.domains", "arbitrum-mainnet.infura.io", + "avalanche-mainnet.infura.io", "authentication.api.cx.metamask.io", "bafkreifvhjdf6ve4jfv6qytqtux5nd4nwnelioeiqx5x2ez5yrgrzk7ypi.ipfs.dweb.link", "bafybeidxfmwycgzcp4v2togflpqh2gnibuexjy4m4qqwxp7nh3jx5zlh4y.ipfs.dweb.link", diff --git a/test/e2e/tests/network/multi-rpc.spec.ts b/test/e2e/tests/network/multi-rpc.spec.ts index 6fc7025f5dbc..2ab43445c484 100644 --- a/test/e2e/tests/network/multi-rpc.spec.ts +++ b/test/e2e/tests/network/multi-rpc.spec.ts @@ -27,7 +27,7 @@ describe('MultiRpc:', function (this: Suite) { json: { id: '1694444405781', jsonrpc: '2.0', - result: '0xa4b1', + result: '0xa86a', }, })), ]; @@ -37,7 +37,7 @@ describe('MultiRpc:', function (this: Suite) { fixtures: new FixtureBuilder({ onboarding: true }) .withNetworkController({ providerConfig: { - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, + rpcPrefs: { blockExplorerUrl: 'https://snowtrace.io/' }, }, networkConfigurations: { networkConfigurationId: { @@ -48,20 +48,20 @@ describe('MultiRpc:', function (this: Suite) { rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, }, '2ce66016-8aab-47df-b27f-318c80865eb0': { - chainId: '0xa4b1', + chainId: '0xa86a', id: '2ce66016-8aab-47df-b27f-318c80865eb0', - nickname: 'Arbitrum mainnet', + nickname: 'Avalanche mainnet', rpcPrefs: {}, - rpcUrl: 'https://arbitrum-mainnet.infura.io', - ticker: 'ETH', + rpcUrl: 'https://avalanche-mainnet.infura.io', + ticker: 'AVAX', }, '2ce66016-8aab-47df-b27f-318c80865eb1': { - chainId: '0xa4b1', + chainId: '0xa86a', id: '2ce66016-8aab-47df-b27f-318c80865eb1', - nickname: 'Arbitrum mainnet 2', + nickname: 'Avalanche mainnet 2', rpcPrefs: {}, rpcUrl: 'https://responsive-rpc.test/', - ticker: 'ETH', + ticker: 'AVAX', }, }, selectedNetworkClientId: 'networkConfigurationId', @@ -81,7 +81,7 @@ describe('MultiRpc:', function (this: Suite) { await driver.delay(regularDelayMs); - // complete + // complete onboarding await driver.clickElement('[data-testid="onboarding-complete-done"]'); // pin extension @@ -96,7 +96,7 @@ describe('MultiRpc:', function (this: Suite) { await driver.clickElement('[data-testid="network-display"]'); await driver.clickElement( - '[data-testid="network-rpc-name-button-0xa4b1"]', + '[data-testid="network-rpc-name-button-0xa86a"]', ); const menuItems = await driver.findElements('.select-rpc-url__item'); @@ -341,7 +341,7 @@ describe('MultiRpc:', function (this: Suite) { json: { id: '1694444405781', jsonrpc: '2.0', - result: '0xa4b1', + result: '0xa86a', }, })), ]; @@ -351,7 +351,7 @@ describe('MultiRpc:', function (this: Suite) { fixtures: new FixtureBuilder({ onboarding: true }) .withNetworkController({ providerConfig: { - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, + rpcPrefs: { blockExplorerUrl: 'https://snowtrace.io/' }, }, networkConfigurations: { networkConfigurationId: { @@ -362,20 +362,20 @@ describe('MultiRpc:', function (this: Suite) { rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, }, '2ce66016-8aab-47df-b27f-318c80865eb0': { - chainId: '0xa4b1', + chainId: '0xa86a', id: '2ce66016-8aab-47df-b27f-318c80865eb0', - nickname: 'Arbitrum mainnet', + nickname: 'Avalanche mainnet', rpcPrefs: {}, - rpcUrl: 'https://arbitrum-mainnet.infura.io', - ticker: 'ETH', + rpcUrl: 'https://avalanche-mainnet.infura.io', + ticker: 'AVAX', }, '2ce66016-8aab-47df-b27f-318c80865eb1': { - chainId: '0xa4b1', + chainId: '0xa86a', id: '2ce66016-8aab-47df-b27f-318c80865eb1', - nickname: 'Arbitrum mainnet 2', + nickname: 'Avalanche mainnet 2', rpcPrefs: {}, rpcUrl: 'https://responsive-rpc.test/', - ticker: 'ETH', + ticker: 'AVAX', }, }, selectedNetworkClientId: 'networkConfigurationId', @@ -395,7 +395,7 @@ describe('MultiRpc:', function (this: Suite) { await driver.delay(regularDelayMs); - // go to advanced settigns + // go to advanced settings await driver.clickElementAndWaitToDisappear({ text: 'Manage default privacy settings', }); @@ -406,7 +406,7 @@ describe('MultiRpc:', function (this: Suite) { // open edit modal await driver.clickElement({ - text: 'arbitrum-mainnet.infura.io', + text: 'avalanche-mainnet.infura.io', tag: 'p', }); @@ -414,7 +414,7 @@ describe('MultiRpc:', function (this: Suite) { await driver.delay(regularDelayMs); await driver.clickElement({ - text: 'Arbitrum mainnet 2', + text: 'Avalanche mainnet 2', tag: 'button', }); @@ -446,14 +446,14 @@ describe('MultiRpc:', function (this: Suite) { // Validate the network was edited const networkEdited = await driver.isElementPresent({ - text: '“Arbitrum One” was successfully edited!', + text: '“Avalanche Network C-Chain” was successfully edited!', }); assert.equal( networkEdited, true, - '“Arbitrum One” was successfully edited!', + '“Avalanche Network C-Chain” was successfully edited!', ); - // Ensures popover backround doesn't kill test + // Ensures popover background doesn't kill test await driver.assertElementNotPresent('.popover-bg'); // We need to use clickElementSafe + assertElementNotPresent as sometimes the network dialog doesn't appear, as per this issue (#27870) @@ -467,12 +467,12 @@ describe('MultiRpc:', function (this: Suite) { await driver.clickElement('[data-testid="network-display"]'); - const arbitrumRpcUsed = await driver.findElement({ - text: 'Arbitrum mainnet 2', + const avalancheRpcUsed = await driver.findElement({ + text: 'Avalanche mainnet 2', tag: 'button', }); - const existRpcUsed = arbitrumRpcUsed !== undefined; + const existRpcUsed = avalancheRpcUsed !== undefined; assert.equal(existRpcUsed, true, 'Second Rpc is used'); }, ); From 31c62b9b107de2f90e14d20fadc040b78acd9528 Mon Sep 17 00:00:00 2001 From: salimtb Date: Thu, 24 Oct 2024 16:16:14 +0200 Subject: [PATCH 3/3] fix: fix PR comments --- ui/pages/onboarding-flow/welcome/welcome.js | 40 ++++++++++++++------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/ui/pages/onboarding-flow/welcome/welcome.js b/ui/pages/onboarding-flow/welcome/welcome.js index 6b2377adf495..b71798f33804 100644 --- a/ui/pages/onboarding-flow/welcome/welcome.js +++ b/ui/pages/onboarding-flow/welcome/welcome.js @@ -110,20 +110,36 @@ export default function OnboardingWelcome() { chainIdsToAdd.includes(network.chainId), ); - for (const network of selectedNetworks) { - await dispatch( - addNetwork({ - chainId: network.chainId, - blockExplorerUrls: network.blockExplorerUrls, - defaultRpcEndpointIndex: network.defaultRpcEndpointIndex, - defaultBlockExplorerUrlIndex: - network.defaultBlockExplorerUrlIndex, - name: network.name, - nativeCurrency: network.nativeCurrency, - rpcEndpoints: network.rpcEndpoints, + try { + await Promise.all( + selectedNetworks.map(async (network) => { + const { + chainId, + blockExplorerUrls, + defaultRpcEndpointIndex, + defaultBlockExplorerUrlIndex, + name, + nativeCurrency, + rpcEndpoints, + } = network; + + await dispatch( + addNetwork({ + chainId, + blockExplorerUrls, + defaultRpcEndpointIndex, + defaultBlockExplorerUrlIndex, + name, + nativeCurrency, + rpcEndpoints, + }), + ); + + console.info(`Successfully added network: ${name}`); }), ); - console.log(`Successfully added network: ${network.name}`); + } catch (error) { + console.error('Error adding networks:', error); } await dispatch(updateNetworksList(networkOrder));