Skip to content

Conversation

salimtb
Copy link
Contributor

@salimtb salimtb commented Sep 11, 2025

Description

When switching account groups, the previously selected network might not exist in the target group (e.g., switching from an EVM-only group to a Solana-only group, or vice-versa). This caused users to land on an unavailable network and occasionally led to empty states or confusing UI.

This PR introduces a safe fallback: if the previously selected network is not part of the new account group, we automatically switch to a “popular” network available in that group. This yields a smoother experience and guarantees a usable default after every account switch.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: Fixed a bug where switching accounts could leave users on an unavailable network; the app now falls back to a popular network available in the new account group.

Related issues

Fixes: #

Manual testing steps

  • select solana network
  • switch to an account without solana
  • you should fallback to all popular network

Screenshots/Recordings

Before

After

Screen.Recording.2025-09-11.at.15.06.07.mov

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs and MetaMask Extension Coding Standards.
  • I've completed the PR template to the best of my ability
  • I’ve included tests if applicable (unit tests for selector/dispatcher logic + integration test for account switch fallback)
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). Not required for external contributors.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot
Copy link
Collaborator

metamaskbot commented Sep 11, 2025

✨ Files requiring CODEOWNER review ✨

🔑 @MetaMask/accounts-engineers (4 files, +203 -4)
  • 📁 ui/
    • 📁 components/
      • 📁 multichain-accounts/
        • 📁 multichain-account-list/
          • 📄 multichain-account-list.test.tsx +123 -1
          • 📄 multichain-account-list.tsx +29 -0
        • 📁 permissions/
          • 📁 multichain-edit-accounts-page/
            • 📄 multichain-edit-accounts-page.test.tsx +24 -0
    • 📁 pages/
      • 📁 multichain-accounts/
        • 📁 multichain-accounts-connect-page/
          • 📄 multichain-accounts-connect-page.test.tsx +27 -3

@metamaskbot
Copy link
Collaborator

📊 Page Load Benchmark Results

Current Commit: 5845824 | Date: 9/11/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.28s (±16ms) 🟡 | historical mean value: 1.31s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 963ms (±14ms) 🟢 | historical mean value: 982ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 91ms (±12ms) 🟢 | historical mean value: 100ms ⬇️ (historical data)
📈 Detailed Results
Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.28s 16ms 1.26s 1.36s 1.31s 1.36s
domContentLoaded 963ms 14ms 945ms 1.04s 986ms 1.04s
firstPaint 91ms 12ms 76ms 140ms 116ms 140ms
firstContentfulPaint 91ms 12ms 76ms 140ms 116ms 140ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms

Results generated automatically by MetaMask CI

@metamaskbot
Copy link
Collaborator

Builds ready [5845824]
UI Startup Metrics (1229 ± 59 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyHomeuiStartup1229111014125912611328
load105893712115710901155
domContentLoaded105193212035710831147
domInteractive18145371737
firstPaint72277116742110811143
backgroundConnect2502372908255263
firstReactRender2716125172842
getState16676111839
initialActions61598719
loadScripts80869593756843897
setupStore1162751125
WebpackHomeuiStartup20051488245724921682445
load15941198192819117201879
domContentLoaded15821185192219217141867
domInteractive171281111440
firstPaint1646341956179279
backgroundConnect3916349523366
firstReactRender91363347683322
getState3553067514274
initialActions62294615
loadScripts15781182191119117121858
setupStore246312561429
FirefoxBrowserifyHomeuiStartup14491238186411515181700
load1248108114227712961383
domContentLoaded1248108114227712941382
domInteractive1043532450115222
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect3521143173767
firstReactRender28235553038
getState11320327742
initialActions31173310
loadScripts1222106214027612771356
setupStore11511313932
WebpackHomeuiStartup16671458213715117621981
load14211234174912915201654
domContentLoaded14211234174912915201653
domInteractive1113536064107297
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect33215473946
firstReactRender46395744853
getState8315516714
initialActions4160648
loadScripts13971212172313014961632
setupStore126145151027
Benchmark value 251 exceeds gate value 10 for chrome browserify home mean backgroundConnect
Benchmark value 28 exceeds gate value 23 for chrome browserify home mean firstReactRender
Benchmark value 17 exceeds gate value 15 for chrome browserify home mean getState
Benchmark value 7 exceeds gate value 1 for chrome browserify home mean initialActions
Benchmark value 264 exceeds gate value 18 for chrome browserify home p95 backgroundConnect
Benchmark value 39 exceeds gate value 33 for chrome browserify home p95 getState
Benchmark value 19 exceeds gate value 1.2 for chrome browserify home p95 initialActions
Benchmark value 25 exceeds gate value 17 for chrome browserify home p95 setupStore
Benchmark value 35 exceeds gate value 29 for chrome webpack home mean getState
Benchmark value 274 exceeds gate value 195 for chrome webpack home p95 getState
Benchmark value 15 exceeds gate value 7 for chrome webpack home p95 initialActions
Benchmark value 1450 exceeds gate value 1405 for firefox browserify home mean uiStartup
Benchmark value 1249 exceeds gate value 1245 for firefox browserify home mean load
Benchmark value 1248 exceeds gate value 1239 for firefox browserify home mean domContentLoaded
Benchmark value 35 exceeds gate value 25 for firefox browserify home mean backgroundConnect
Benchmark value 29 exceeds gate value 25 for firefox browserify home mean firstReactRender
Benchmark value 4 exceeds gate value 1 for firefox browserify home mean initialActions
Benchmark value 11 exceeds gate value 9 for firefox browserify home mean setupStore
Benchmark value 1700 exceeds gate value 1660 for firefox browserify home p95 uiStartup
Benchmark value 222 exceeds gate value 195 for firefox browserify home p95 domInteractive
Benchmark value 42 exceeds gate value 24 for firefox browserify home p95 getState
Benchmark value 10 exceeds gate value 2 for firefox browserify home p95 initialActions
Benchmark value 32 exceeds gate value 27 for firefox browserify home p95 setupStore
Benchmark value 1667 exceeds gate value 1615 for firefox webpack home mean uiStartup
Benchmark value 1422 exceeds gate value 1380 for firefox webpack home mean load
Benchmark value 1421 exceeds gate value 1380 for firefox webpack home mean domContentLoaded
Benchmark value 112 exceeds gate value 100 for firefox webpack home mean domInteractive
Benchmark value 33 exceeds gate value 26 for firefox webpack home mean backgroundConnect
Benchmark value 46 exceeds gate value 38 for firefox webpack home mean firstReactRender
Benchmark value 5 exceeds gate value 1 for firefox webpack home mean initialActions
Benchmark value 1398 exceeds gate value 1360 for firefox webpack home mean loadScripts
Benchmark value 1981 exceeds gate value 1935 for firefox webpack home p95 uiStartup
Benchmark value 297 exceeds gate value 156 for firefox webpack home p95 domInteractive
Benchmark value 53 exceeds gate value 50 for firefox webpack home p95 firstReactRender
Benchmark value 8 exceeds gate value 2 for firefox webpack home p95 initialActions
Benchmark value 1632 exceeds gate value 1630 for firefox webpack home p95 loadScripts
Sum of mean exceeds: 541ms | Sum of p95 exceeds: 660.8ms
Sum of all benchmark exceeds: 1201.8ms

Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 43 Bytes (0%)
  • ui: 870 Bytes (0.01%)
  • common: 10 Bytes (0%)

@github-actions github-actions bot added size-M and removed size-S labels Sep 11, 2025
@salimtb salimtb marked this pull request as ready for review September 11, 2025 13:08
@salimtb salimtb requested a review from a team as a code owner September 11, 2025 13:08
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

bergarces
bergarces previously approved these changes Sep 11, 2025
@metamaskbot
Copy link
Collaborator

📊 Page Load Benchmark Results

Current Commit: 7427c0d | Date: 9/11/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.28s (±16ms) 🟡 | historical mean value: 1.30s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 964ms (±13ms) 🟢 | historical mean value: 979ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 92ms (±14ms) 🟢 | historical mean value: 100ms ⬇️ (historical data)
📈 Detailed Results
Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.28s 16ms 1.26s 1.37s 1.31s 1.37s
domContentLoaded 964ms 13ms 948ms 1.04s 986ms 1.04s
firstPaint 92ms 14ms 80ms 176ms 116ms 176ms
firstContentfulPaint 92ms 14ms 80ms 176ms 116ms 176ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms

Results generated automatically by MetaMask CI

@metamaskbot
Copy link
Collaborator

Builds ready [7427c0d]
UI Startup Metrics (1245 ± 66 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyHomeuiStartup1245109913966612921357
load108395312376311281186
domContentLoaded107494612296311181176
domInteractive18144981743
firstPaint76088124042911081182
backgroundConnect2472322818251259
firstReactRender24164062539
getState1356791727
initialActions50588612
loadScripts83571398462879938
setupStore961921014
WebpackHomeuiStartup22201585289827523832728
load17601213225322319002108
domContentLoaded17511203222422118912091
domInteractive25141452916115
firstPaint1776943570205327
backgroundConnect3718308313675
firstReactRender1004235077100325
getState3953017718274
initialActions13229138717
loadScripts17461199221122018882077
setupStore257302541728
FirefoxBrowserifyHomeuiStartup14221269182311414681665
load1227107314027812891372
domContentLoaded1226107314027812891371
domInteractive1083328552116246
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect331997133464
firstReactRender28236772945
getState9314820717
initialActions619913331
loadScripts1201105413687412631327
setupStore12420224833
WebpackHomeuiStartup15271332184413016501785
load13061135159912014191534
domContentLoaded13061135159812014181533
domInteractive1013132253100276
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect31185673642
firstReactRender42346254552
getState6398969
initialActions3116235
loadScripts12831115157712113911515
setupStore95244919
Benchmark value 1246 exceeds gate value 1234 for chrome browserify home mean uiStartup
Benchmark value 1083 exceeds gate value 1070 for chrome browserify home mean load
Benchmark value 1075 exceeds gate value 1061 for chrome browserify home mean domContentLoaded
Benchmark value 247 exceeds gate value 10 for chrome browserify home mean backgroundConnect
Benchmark value 24 exceeds gate value 23 for chrome browserify home mean firstReactRender
Benchmark value 6 exceeds gate value 1 for chrome browserify home mean initialActions
Benchmark value 836 exceeds gate value 830 for chrome browserify home mean loadScripts
Benchmark value 43 exceeds gate value 41 for chrome browserify home p95 domInteractive
Benchmark value 1183 exceeds gate value 1180 for chrome browserify home p95 firstPaint
Benchmark value 260 exceeds gate value 18 for chrome browserify home p95 backgroundConnect
Benchmark value 12 exceeds gate value 1.2 for chrome browserify home p95 initialActions
Benchmark value 2220 exceeds gate value 2192 for chrome webpack home mean uiStartup
Benchmark value 1760 exceeds gate value 1711 for chrome webpack home mean load
Benchmark value 1751 exceeds gate value 1704 for chrome webpack home mean domContentLoaded
Benchmark value 25 exceeds gate value 21 for chrome webpack home mean domInteractive
Benchmark value 39 exceeds gate value 29 for chrome webpack home mean getState
Benchmark value 13 exceeds gate value 7 for chrome webpack home mean initialActions
Benchmark value 1746 exceeds gate value 1699 for chrome webpack home mean loadScripts
Benchmark value 2728 exceeds gate value 2454 for chrome webpack home p95 uiStartup
Benchmark value 2108 exceeds gate value 2030 for chrome webpack home p95 load
Benchmark value 2091 exceeds gate value 2005 for chrome webpack home p95 domContentLoaded
Benchmark value 115 exceeds gate value 57 for chrome webpack home p95 domInteractive
Benchmark value 274 exceeds gate value 195 for chrome webpack home p95 getState
Benchmark value 17 exceeds gate value 7 for chrome webpack home p95 initialActions
Benchmark value 2077 exceeds gate value 1970 for chrome webpack home p95 loadScripts
Benchmark value 1423 exceeds gate value 1405 for firefox browserify home mean uiStartup
Benchmark value 33 exceeds gate value 25 for firefox browserify home mean backgroundConnect
Benchmark value 28 exceeds gate value 25 for firefox browserify home mean firstReactRender
Benchmark value 6 exceeds gate value 1 for firefox browserify home mean initialActions
Benchmark value 13 exceeds gate value 9 for firefox browserify home mean setupStore
Benchmark value 1665 exceeds gate value 1660 for firefox browserify home p95 uiStartup
Benchmark value 246 exceeds gate value 195 for firefox browserify home p95 domInteractive
Benchmark value 31 exceeds gate value 2 for firefox browserify home p95 initialActions
Benchmark value 33 exceeds gate value 27 for firefox browserify home p95 setupStore
Benchmark value 102 exceeds gate value 100 for firefox webpack home mean domInteractive
Benchmark value 32 exceeds gate value 26 for firefox webpack home mean backgroundConnect
Benchmark value 43 exceeds gate value 38 for firefox webpack home mean firstReactRender
Benchmark value 3 exceeds gate value 1 for firefox webpack home mean initialActions
Benchmark value 276 exceeds gate value 156 for firefox webpack home p95 domInteractive
Benchmark value 52 exceeds gate value 50 for firefox webpack home p95 firstReactRender
Benchmark value 5 exceeds gate value 2 for firefox webpack home p95 initialActions
Sum of mean exceeds: 532ms | Sum of p95 exceeds: 1165.8ms
Sum of all benchmark exceeds: 1697.8ms

Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 43 Bytes (0%)
  • ui: 1.03 KiB (0.01%)
  • common: 10 Bytes (0%)


const btcAccountGroup = useSelector((state) =>
getInternalAccountBySelectedAccountGroupAndCaip(state, BtcScope.Mainnet),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just don't like the fact that this now needs to live inside UI component for rendering the chain-agnostic account list (we call multichain account list).

Can this live somewhere else? Inside some service, or a related controller? Can you take this data from somewhere else and just dispatch your action that you need from our setSelectedMultichainAccount action?

Here we do the selection:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To elaborate more on why I'm asking you this.

So, the multichain account list and multichain account page was devised to be abstract enough, to work only with the Account Tree which represents our chain-agnostic (multichain) architecture, and on the way while building it, we wanted to respect it and keep it chain-agnostic completely, which means no internal accounts or similar specific chain related stuff is required. This led us to having abstracted multichain account list, easy to maintain and more performant without having to deal with internal accounts or additional processes.

What is done here is because of the relation between the account selection and networks. Why this cannot be implemented somewhere outside of this UI component? This sounds like something should listen for a change and take some action, maybe some service or controller can handle this?

This is not only adding more complexity inside our UI component, but it is also putting a lot more redundant pressure on the account list (processes that are not needed all the time). So, every time somebody opens the account list, this will be pulling internal accounts and processing some logic that might not be used by the user at all. It just loads somebody's machine for nothing most of the time.

Can you please consider having another approach for this? 🙏

Copy link
Contributor Author

@salimtb salimtb Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@david0xd I agree with you, this logic should be at the accountsTreeController or accountsController level. On every account switch, we should filter the network state (MultichainNetworkController, NetworkController, NetworkEnablementController) to ensure the selected network matches the chains (EVM / Sol / BTC) available in the account group.

Since we don’t currently do this, we end up with issues like the one. For example, try selecting an account that doesn’t include Solana in its group. If you download the state logs and inspect multichainNetworkConfigurationsByChainId, you’ll see the Solana network still present when it shouldn’t be. That’s one of the reasons we’re hitting this edge case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @ccharly have an idea about it if you think this can be solved on the account tree controller or similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants