Skip to content

feat: add account ordering to Multichain API #41068

Merged
jiexi merged 42 commits into
mainfrom
jl/wallet_sessionChanged-account-ordering-2
Apr 2, 2026
Merged

feat: add account ordering to Multichain API #41068
jiexi merged 42 commits into
mainfrom
jl/wallet_sessionChanged-account-ordering-2

Conversation

@jiexi

@jiexi jiexi commented Mar 19, 2026

Copy link
Copy Markdown
Member

Description

Adds ordering to the accounts property in the wallet_getSession response, wallet_createSession response, and wallet_sessionChanged event. Accounts are now ordered by most recently used (lastSelected)

This is needed as pre-req work to getting rid of ecosystem specific (solana, tron, other multichain) metamask_accountsChanged events.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: null

Not end user facing.

Related issues

Core: MetaMask/core#8255

Manual testing steps

  1. Visit https://metamask.github.io/test-dapp-multichain/latest/
  2. Connect 3 accounts (easiest to only connect one network at time for validation)
  3. Change the active account via the wallet UI
  4. Note each time you change that the account list reorders in the dapp
  5. Check the wallet_sessionChanged events too for ordering
  6. Refresh
  7. The account order should still be the same as before refreshing
  8. Revoke the session
  9. Connect the same 3 accounts again to the same network
  10. The account order should still be the same as before revoking

Screenshots/Recordings

Before

After

Screen.Recording.2026-03-26.at.10.33.36.AM.mov

Pre-merge author checklist

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.

Note

Medium Risk
Touches Multichain API session notifications and adds a delayed wallet_sessionChanged broadcast, which could affect dapp session state timing/order. Dependency bumps to @metamask/multichain-api-middleware and @metamask/chain-agnostic-permission may introduce behavior changes in session scope shaping.

Overview
Adds deterministic ordering to Multichain API session accounts by sorting CAIP account IDs using each address’s lastSelected rank, and wires this sorter into getSessionScopes for wallet_getSession, wallet_createSession, and wallet_sessionChanged.

Also triggers a wallet_sessionChanged notification to all authorized origins on selectedAccountGroupChange (currently via a temporary 1s setTimeout workaround), with new/updated unit tests covering both the sorting behavior and the broadcast.

Written by Cursor Bugbot for commit 28e58b8. This will update automatically on new commits. Configure here.

Comment thread app/scripts/metamask-controller.js Outdated
ffmcgee725
ffmcgee725 previously approved these changes Mar 20, 2026
Comment thread app/scripts/metamask-controller.test.js
Comment thread package.json Outdated
Comment thread package.json Outdated
Comment thread app/scripts/metamask-controller.js
@jiexi jiexi added team-wallet-integrations Wallet Integrations team no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed labels Mar 23, 2026
this.permissionController.state,
);

// TODO: Remove this setTimeout once https://github.com/MetaMask/core/pull/8261 is released

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MetaMask/core#8261 is merged but we will need to wait until its released (supposed happening this Friday 3/27/26)

github-merge-queue Bot pushed a commit to MetaMask/core that referenced this pull request Mar 24, 2026
…tSession, and wallet_createSession (#8255)

## Explanation

### `@metamask/chain-agnostic-permission`
- **Added** optional `sortAccountIdsByLastSelected` parameter to
`getSessionScopes` function
  - Type: `(accounts: CaipAccountId[]) => CaipAccountId[]`
- When provided, sorts the `accounts` array within each scope (both
required and optional scopes)
  - When not provided, preserves original account order

### `@metamask/multichain-api-middleware`
- **Added** required `sortAccountIdsByLastSelected` hook to
`wallet_getSession` handler
  - Passes the hook to `getSessionScopes` when building session response
- **Added** required `sortAccountIdsByLastSelected` hook to
`wallet_createSession` handler
- Passes the hook to `getSessionScopes` when building session scopes for
approval

## References

See: MetaMask/metamask-extension#41068

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes handler hook contracts in
`multichain-api-middleware` (breaking for integrators) and alters
ordering of accounts returned in session scopes, which could impact
downstream UI/logic expectations.
> 
> **Overview**
> Adds an optional `sortAccountIdsByLastSelected` hook to
`@metamask/chain-agnostic-permission` `getSessionScopes`, applying it to
the `accounts` list for every merged scope before returning session
scopes.
> 
> Updates `wallet_getSession`, `wallet_createSession`, and
`wallet_invokeMethod` handlers in `@metamask/multichain-api-middleware`
to require and pass through this new sorting hook, with corresponding
test updates and changelog entries noting the breaking hook requirement.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4fc3d03. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Alex Mendonca <alex.mendonca@consensys.net>
Co-authored-by: Alex Donesky <adonesky@gmail.com>
Comment thread package.json Outdated
@socket-security

socket-security Bot commented Mar 26, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​metamask/​multichain-api-middleware@​1.2.7 ⏵ 2.0.09910076 +195100
Updated@​metamask/​chain-agnostic-permission@​1.4.0 ⏵ 1.5.010010078 +194 +1100

View full report

@socket-security

socket-security Bot commented Mar 26, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

Comment thread app/scripts/metamask-controller.js

@adonesky1 adonesky1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I agree however that we should switch the wallet-standard package separately to isolate the commit from other unrelated changes in that package

sortedAddresses.map((address, index) => [address, index]),
);

return [...accountIds].sort(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

right?

Suggested change
return [...accountIds].sort(
return accountIds.sort(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

.sort() isn't pure and modifies the array in place.

If we make this change, then i'd prefer to not return anything at all to make it clear that the original array is getting mutated, but that would also require changes to the middleware and permission packages again

adonesky1
adonesky1 previously approved these changes Apr 1, 2026
@adonesky1

Copy link
Copy Markdown
Contributor

@metamaskbot update-policies

1 similar comment
@jiexi

jiexi commented Apr 1, 2026

Copy link
Copy Markdown
Member Author

@metamaskbot update-policies

@metamaskbot

Copy link
Copy Markdown
Collaborator

Policies updated.
👀 Please review the diff for suspicious new powers.

Tip

Follow the policy review process outlined in the LavaMoat Policy Review Process doc before expecting an approval from Policy Reviewers.
🧠 Learn how to read policy diffs: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff

✅ lavamoat/browserify/beta/policy.json changes match main/policy.json policy changes
✅ lavamoat/browserify/experimental/policy.json changes match main/policy.json policy changes
✅ lavamoat/browserify/flask/policy.json changes match main/policy.json policy changes
✅ lavamoat/webpack/mv2/beta/policy.json changes match mv2/main/policy.json policy changes
✅ lavamoat/webpack/mv2/experimental/policy.json changes match mv2/main/policy.json policy changes
✅ lavamoat/webpack/mv2/flask/policy.json changes match mv2/main/policy.json policy changes
✅ lavamoat/webpack/mv3/beta/policy.json changes match mv3/main/policy.json policy changes
✅ lavamoat/webpack/mv3/experimental/policy.json changes match mv3/main/policy.json policy changes
✅ lavamoat/webpack/mv3/flask/policy.json changes match mv3/main/policy.json policy changes

@metamaskbot

Copy link
Copy Markdown
Collaborator

Policy update failed. You can review the logs or retry the policy update here

@metamaskbotv2

metamaskbotv2 Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
Builds ready [faf3c5e]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 23eef3f | Date: 8/29/58219 | Pipeline: 23863722062 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -39%
  • loadNewAccount/total: -39%
  • bridgeUserActions/bridge_load_asset_picker: +27%
  • bridgeUserActions/total: +12%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/initialActions: -38%
  • startupPowerUserHome/backgroundConnect: -53%
  • startupPowerUserHome/initialActions: -17%
  • startupPowerUserHome/numNetworkReqs: +74%
  • startupPowerUserHome/uiStartup: -10%
  • startupPowerUserHome/firstPaint: -21%
  • startupPowerUserHome/numNetworkReqs: +23%
  • startupStandardHome/domInteractive: +14%
  • startupStandardHome/initialActions: +25%
  • startupStandardHome/setupStore: -11%
  • startupPowerUserHome/domInteractive: +17%
  • startupPowerUserHome/setupStore: -21%
  • startupStandardHome/backgroundConnect: +14%
  • startupStandardHome/initialActions: -29%
  • startupStandardHome/setupStore: +103%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/metricsToWalletReadyScreen: -19%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +57%
  • onboardingImportWallet/total: -38%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -20%
  • onboardingNewWallet/doneButtonToAssetList: -30%
  • onboardingNewWallet/total: -24%
  • assetDetails/assetClickToPriceChart: +10%
  • assetDetails/total: +10%
  • solanaAssetDetails/assetClickToPriceChart: -60%
  • solanaAssetDetails/total: -60%
  • importSrpHome/loginToHomeScreen: +19%
  • importSrpHome/homeAfterImportWithNewWallet: -49%
  • importSrpHome/total: -43%
  • swap/openSwapPageFromHome: -84%
  • swap/fetchAndDisplaySwapQuotes: +28%
🌐 Dapp Page Load Benchmarks

Current Commit: faf3c5e | Date: 4/1/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.03s (±52ms) 🟡 | historical mean value: 1.03s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 725ms (±50ms) 🟢 | historical mean value: 727ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 84ms (±9ms) 🟢 | historical mean value: 86ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.03s 52ms 1000ms 1.31s 1.12s 1.31s
domContentLoaded 725ms 50ms 695ms 994ms 784ms 994ms
firstPaint 84ms 9ms 64ms 148ms 96ms 148ms
firstContentfulPaint 84ms 9ms 64ms 148ms 96ms 148ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 960 Bytes (0.02%)
  • ui: 174 Bytes (0%)
  • common: 145 Bytes (0%)

@sonarqubecloud

sonarqubecloud Bot commented Apr 1, 2026

Copy link
Copy Markdown

@metamaskbotv2

metamaskbotv2 Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
Builds ready [28e58b8]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 084a8a9 | Date: 9/20/58219 | Pipeline: 23865721292 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -56%
  • loadNewAccount/total: -56%
  • bridgeUserActions/bridge_load_asset_picker: +15%
  • bridgeUserActions/bridge_search_token: -15%
  • bridgeUserActions/total: +14%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/initialActions: -29%
  • startupPowerUserHome/backgroundConnect: -29%
  • startupPowerUserHome/initialActions: -17%
  • startupPowerUserHome/numNetworkReqs: +44%
  • startupStandardHome/firstPaint: -18%
  • startupPowerUserHome/numNetworkReqs: -17%
  • startupStandardHome/initialActions: +25%
  • startupPowerUserHome/setupStore: +36%
  • startupStandardHome/initialActions: -38%
  • startupStandardHome/setupStore: -20%
  • startupPowerUserHome/domInteractive: -11%
  • startupPowerUserHome/backgroundConnect: +14%
  • startupPowerUserHome/setupStore: -20%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/metricsToWalletReadyScreen: -12%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +43%
  • onboardingImportWallet/total: -40%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +12%
  • onboardingNewWallet/doneButtonToAssetList: -23%
  • onboardingNewWallet/total: -18%
  • assetDetails/assetClickToPriceChart: -15%
  • assetDetails/total: -15%
  • solanaAssetDetails/assetClickToPriceChart: -63%
  • solanaAssetDetails/total: -63%
  • importSrpHome/loginToHomeScreen: +14%
  • importSrpHome/openAccountMenuAfterLogin: -22%
  • importSrpHome/homeAfterImportWithNewWallet: -44%
  • importSrpHome/total: -38%
  • swap/openSwapPageFromHome: -85%
  • swap/fetchAndDisplaySwapQuotes: +28%
🌐 Dapp Page Load Benchmarks

Current Commit: 28e58b8 | Date: 4/1/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.01s (±71ms) 🟡 | historical mean value: 1.03s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 713ms (±69ms) 🟢 | historical mean value: 726ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 83ms (±11ms) 🟢 | historical mean value: 86ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.01s 71ms 964ms 1.29s 1.24s 1.29s
domContentLoaded 713ms 69ms 674ms 983ms 918ms 983ms
firstPaint 83ms 11ms 68ms 172ms 100ms 172ms
firstContentfulPaint 83ms 11ms 68ms 172ms 100ms 172ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 960 Bytes (0.02%)
  • ui: 5 Bytes (0%)
  • common: 145 Bytes (0%)

@jiexi jiexi enabled auto-merge April 1, 2026 20:59

@wenfix wenfix left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@jiexi jiexi added this pull request to the merge queue Apr 2, 2026
Merged via the queue into main with commit 7d06f06 Apr 2, 2026
391 of 394 checks passed
@jiexi jiexi deleted the jl/wallet_sessionChanged-account-ordering-2 branch April 2, 2026 15:26
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 2, 2026
@metamaskbot metamaskbot added the release-13.26.0 Issue or pull request that will be included in release 13.26.0 label Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed release-13.26.0 Issue or pull request that will be included in release 13.26.0 size-M team-wallet-integrations Wallet Integrations team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants