Skip to content

release: 13.51.0 - #46634

Merged
sleepytanya merged 131 commits into
stablefrom
release/13.51.0
Oct 9, 2026
Merged

sleepytanya merged 131 commits into
stablefrom
release/13.51.0

Conversation

@metamaskbot

@metamaskbot metamaskbot commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

🚀 v13.51.0 Testing & Release Quality Process

Hi Team,
As part of our new MetaMask Release Quality Process, here’s a quick overview of the key processes, testing strategies, and milestones to ensure a smooth and high-quality deployment.


📋 Key Processes

Testing Strategy

  • Developer Teams:
    Conduct regression and exploratory testing for your functional areas, including automated and manual tests for critical workflows.
  • QA Team:
    Focus on exploratory testing across the wallet, prioritize high-impact areas, and triage any Sentry errors found during testing.
  • Customer Success Team:
    Validate new functionalities and provide feedback to support release monitoring.

GitHub Signoff

  • Each team must sign off on the Release Candidate (RC) via GitHub by the end of the validation timeline (Tuesday EOD PT).
  • Ensure all tests outlined in the Testing Plan are executed, and any identified issues are addressed.

Issue Resolution

  • Resolve all Release Blockers (Sev0 and Sev1) by Tuesday EOD PT.
  • For unresolved blockers, PRs may be reverted, or feature flags disabled to maintain release quality and timelines.

Cherry-Picking Criteria

  • Only critical fixes meeting outlined criteria will be cherry-picked.
  • Developers must ensure these fixes are thoroughly reviewed, tested, and merged by Tuesday EOD PT.

🗓️ Timeline and Milestones

  1. Today (Friday): Begin Release Candidate validation.
  2. Tuesday EOD PT: Finalize RC with all fixes and cherry-picks.
  3. Wednesday: Buffer day for final checks.
  4. Thursday: Submit release to app stores and begin rollout to 1% of users.
  5. Monday: Scale deployment to 10%.
  6. Tuesday: Full rollout to 100%.

✅ Signoff Checklist

Each team is responsible for signing off via GitHub. Use the checkbox below to track signoff completion:

Team sign-off checklist

  • Accounts
  • Assets
  • Bots Team
  • Confirmations
  • Core Extension UX
  • Core Platform
  • Delegation
  • Design System
  • Earn
  • Emerging Opportunities
  • Engagement
  • Extension Platform
  • Mobile UX
  • Money Movement
  • Networks
  • Onboarding
  • Perps
  • Product Safety
  • Subscriptions
  • Swaps and Bridge
  • team-extension-ux

This process is a major step forward in ensuring release stability and quality. Let’s stay aligned and make this release a success! 🚀

Feel free to reach out if you have questions or need clarification.

Many thanks in advance

Reference

Kriys94 and others added 30 commits September 17, 2026 23:46
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Video about how to change API endpoint:
https://www.loom.com/share/3cbc2fda74454e809ad64bfbea116c7d

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how authenticated core-backend clients are configured for
assets/DeFi flows; mis-set env vars could point wallets at wrong
backends or omit auth in production builds.
> 
> **Overview**
> Adds **build-time env overrides** for core-backend REST services
(accounts, prices, token/tokens APIs) via
`shared/lib/core-backend-api-urls`, documented in `.metamaskrc.dist` and
wired through `builds.yml`.
> 
> **Assets** and **DeFi positions v2** init now spread
`getBackendApiUrlsOption()` into `createApiPlatformClient` and skip
`AuthenticationController:getBearerToken` when
`MM_BACKEND_DISABLE_AUTH=true`, so dev/local backends that reject
production tokens can be used with the URL overrides.
> 
> Includes a unit test for the disabled-auth path in assets init and
**LavaMoat webpack policy** updates (scoped dependency entries) aligned
with the dependency graph.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
91f9c62. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Co-authored-by: Davide Brocchetto <davide.brocchetto@consensys.net>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
…issing (#46420)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

When Accounts API `valueTransfers[].decimal` is missing, Activity scaled
raw base-unit amounts with `decimals ?? 0` — e.g. a 167.1211 USDT send
rendered as `-167.12M` with fiat from the same unscaled number. Map-time
fail-closed / `getKnownTokenDecimals` landed in core
([#10256](MetaMask/core#10256)) but is not
published yet; Extension is still on `@metamask/client-utils@2.0.2`.

This PR is a **minimal display-layer safety net** only (parity with
[Mobile #36406](MetaMask/metamask-mobile#36406);
no temporary recovery to remove later):

1. `getHumanReadableTokenAmount` returns `undefined` for EVM mapper
amounts (`assetType: 'erc20' | 'native'`) with unknown decimals — amount
and derived fiat are omitted instead of inflated.
2. When amount is omitted **and** scale is unknown (`hasUnknownScale`),
returns `undefined` instead of inventing `'0'`, so fail-closed mapper
rows do not show fake zero transfers. Zero-value natives that omit
amount but keep known decimals (and `assetType` since client-utils
1.3.0) still render as `0` / `-0 ETH`.

Ramp / keyring human-readable amounts (no `assetType`) are unchanged.
Zero-value native metadata-only rows without `assetType` still render as
`0`.

**Follow-up after client-utils publish + Extension bump:** wire optional
`getKnownTokenDecimals` at mapper call sites for recovery. This display
guard can stay as belt-and-suspenders.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: Fixed Activity showing hugely inflated token and fiat
amounts when transfer decimals were missing

## **Related issues**

Fixes:
[TMCU-1303](https://consensyssoftware.atlassian.net/browse/TMCU-1303)

## **Manual testing steps**

1. Build and load the extension (`yarn start`) with Activity Redesign
enabled.
2. Send an ERC-20 whose Accounts API payload includes `decimal`
enrichment; open Activity list and details; verify human-readable amount
and fiat render correctly.
3. Reproduce or fixture a transfer whose payload omits `decimal` (or
inspect a known missing-decimals capture); open Activity list/details
for that transfer.
4. Verify the amount is blank (not inflated like `167.12M`, not fake
`0`), and fiat is not computed from unscaled base units.
5. Confirm a recent ramp buy/sell and a Solana/non-EVM transfer still
show human-readable amounts.
6. Confirm a zero-value contract interaction / send (amount omitted,
decimals known, including with `assetType: 'native'`) still shows `0` /
`-0 ETH`.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.


[TMCU-1303]:
https://consensyssoftware.atlassian.net/browse/TMCU-1303?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Display-only guard in Activity amount formatting with targeted EVM
`assetType` checks; intentional blank amounts replace incorrect inflated
or zero values.
> 
> **Overview**
> Activity was treating missing `decimals` as **0** for EVM-mapped
transfers (`assetType: 'erc20' | 'native'`), so raw base-unit values
showed up as huge token amounts and drove inflated fiat.
> 
> **`getHumanReadableTokenAmount`** now **returns `undefined`** when EVM
mapper tokens have unknown scale (no `decimals` with `assetType`), and
when the amount is missing but `assetType` is set—so the UI omits the
amount/fiat instead of showing inflated numbers or a fake **`0`**.
Ramp/keyring rows without `assetType` still pass through human-readable
values; legacy zero-value native rows without `assetType` still render
as **`0`**.
> 
> Tests cover fail-closed omission, unknown-decimals EVM amounts, and
unchanged non-EVM/human-readable paths.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
81227ba. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ings (#46443)

## Summary
Fix for
[#46453](#46453)
- Slippage values above 100% are meaningless in DeFi — 100% already
means "accept any price, including receiving nothing". Allowing values
above this threshold could confuse users or silently mask bugs in
downstream price calculations.
- The Bridge transaction settings modal
(`bridge-transaction-settings-modal.tsx`) now clamps the input value to
`'100'` on every keystroke and applies a `Math.min(newSlippage, 100)`
guard on submit.
- The Swaps transaction settings (`transaction-settings.js`) applies the
same clamp on the `onChange` handler.
- A new E2E test (`swaps-notifications.spec.ts`) verifies that entering
`101` results in `100` being displayed and used.

Before: 
https://www.loom.com/share/25b31e0c7eca4fcebf7389bd3971b0e7
Now:
https://www.loom.com/share/3ad5ef977862456c80c4e59ad7a5ad7e

## Changes

| File | Change |
|------|--------|
| `ui/pages/bridge/prepare/bridge-transaction-settings-modal.tsx` |
Clamp input to 100 on change + Math.min guard on submit |
| `ui/pages/swaps/transaction-settings/transaction-settings.js` | Clamp
input to 100 on change |
| `test/e2e/page-objects/pages/bridge/quote-page.ts` | Add
`checkCustomSlippageDisplayedValue` helper + `submitSlippageModal` |
| `test/e2e/tests/swaps/swaps-notifications.spec.ts` | Add E2E test
asserting 101 is clamped to 100 |

## Test plan

- [ ] Open Bridge flow → Transaction Settings → enter `101` as custom
slippage → confirm the field shows `100`
- [ ] Open Swaps flow → Transaction Settings → enter `101` as custom
slippage → confirm the field shows `100`
- [ ] Run the new E2E test: `yarn test:e2e --grep "clamps slippage to
100"`
- [ ] Verify entering `99` or `0.5` still works correctly (no unintended
clamping)
- [ ] Verify existing slippage warning tests still pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes slippage validation and Redux error-key behavior in
swap/bridge flows, which can affect whether quotes proceed, though the
cap aligns with existing max-tolerance semantics.
> 
> **Overview**
> Custom slippage above **100%** is now treated as invalid for both
**Bridge** and **Swaps** transaction settings, since higher values do
not change meaning and can mislead users or downstream quote logic.
> 
> In the **Bridge** settings modal, blur on the custom field applies
`Math.min(..., 100)`, updates the custom control to **100%**, and shows
a warning using the new **`swapSlippageCappedDescription`** copy. The
custom input also gets **`inputmode="decimal"`**.
> 
> **Swaps** settings cap on blur/Enter via
**`finishEnteringCustomValue`**, set **`isSlippageCapped`**, and surface
the same capped message (with existing over-limit title) while keeping
other low/high/negative warnings in a extracted
**`getSlippageNotification`** helper. Preset buttons clear the capped
state; error-key dispatch is driven off that helper.
> 
> Coverage adds locale strings, bridge/swaps unit expectations, bridge
page-object helpers, and an E2E case that entering **101** displays
**100%**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1f7b422. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: javiergarciavera <javiergarciavera@users.noreply.github.com>
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

After a conversation between Networks, DataLake and API teams, the fix
is moved to the backed side (basically the WS publishes the updates for
the slip44 USDC representation on Arc instead of the ERC20 one).
This PR reverts the changes in #46259 not needed anymore

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: revert forced fetch for Arc assets

## **Related issues**

Fixes:
[WPN-2105](https://consensyssoftware.atlassian.net/browse/WPN-2105)

## **Manual testing steps**

### Arc swaps

1. On Arc, confirm the starting balances for EURC and native USDC.
2. Swap EURC -> USDC.
3. After the transaction is confirmed, verify:
   - EURC decreased.
   - native USDC increased.
- the USDC balance shown in the wallet matches the expected post-swap
balance.
4. Swap USDC -> EURC.
5. After the transaction is confirmed, verify:
   - native USDC decreased.
   - EURC increased.
- both balances update automatically from the confirmed
transaction/WebSocket update.

### Ethereum swaps

1. On Ethereum, confirm the starting balances for ETH and USDC.
2. Swap ETH -> USDC.
3. After confirmation, verify:
   - ETH decreased, including gas.
   - USDC increased.
4. Swap USDC -> ETH.
5. After confirmation, verify:
   - USDC decreased.
   - ETH increased, accounting for gas.
6. Pick any ERC20 token supported by the swap flow.
7. Swap that ERC20 token -> USDC.
8. After confirmation, verify:
   - the ERC20 token decreased.
   - USDC increased.
9. Swap USDC -> the same ERC20 token.
10. After confirmation, verify:
   - USDC decreased.
   - the ERC20 token increased.

### Bridge

1. Confirm the starting USDC balances on Ethereum and Arc.
2. Bridge USDC.ETH -> USDC.ARC.
3. After the bridge completes, verify:
   - Ethereum USDC decreased.
   - Arc native USDC increased.
   - Arc USDC updates automatically in the wallet.
4. Bridge USDC.ARC -> USDC.ETH.
5. After the bridge completes, verify:
   - Arc native USDC decreased.
   - Ethereum USDC increased.
   - both network balances match the expected post-bridge state.

### Regression check

1. Repeat at least one Arc swap while DevTools is open.
2. Verify there is no extension-side forced asset refresh triggered from
`TransactionController:transactionConfirmed`.
3. Verify the Arc native USDC balance still updates correctly after
confirmation.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [X] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [X] I've completed the PR template to the best of my ability
- [X] I’ve included tests if applicable
- [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [X] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [X] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [X] 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.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Removes a client-side recovery path for stale Arc native USDC
balances; correctness now depends on backend WS updates being in place
and reliable.
> 
> **Overview**
> Reverts the extension-side workaround that listened for
**`TransactionController:transactionConfirmed`** on Arc and triggered a
forced **`getAssets`** refresh (with **`forceUpdate`** and
**`bypassServerCache`**) for the sending account in the selected group.
> 
> That logic lived in **`assets-controller-init.ts`** as
**`subscribeToArcTransactionConfirmedRefresh`** and is removed along
with its Arc-specific imports and constants. **`AssetsControllerInit`**
no longer registers that subscriber after controller creation.
> 
> The matching unit tests in **`assets-controller-init.test.ts`**
(including the **`getAssets`** mock) are deleted so init tests no longer
cover Arc confirmation-driven refreshes.
> 
> Arc native USDC balance updates after swaps/bridges are expected to
come from backend/WebSocket (slip44 USDC) instead of this client fetch.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
59f4f41. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->



[WPN-2105]:
https://consensyssoftware.atlassian.net/browse/WPN-2105?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Migrates MetaMetrics persistence, traces, and unlock-wallet E2E specs to
Playwright.

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

Fixes:
https://consensyssoftware.atlassian.net/browse/MMQA-2259

## **Manual testing steps**

CI should be green

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to E2E harness proxy wiring and test migrations;
no production analytics behavior is modified in the diff.
> 
> **Overview**
> Moves MetaMetrics E2E coverage from Mocha/Selenium to Playwright by
adding `*.pw.spec.ts` for **App Opened**, **Sentry traces**, **unlock
wallet page events**, and updating **Delete MetaMetrics Data** to use
`E2E_DRIVER.PLAYWRIGHT`, while removing the old `*.spec.ts` files
(including **Sessions**, which is dropped in this PR with no Playwright
replacement).
> 
> Fixes flaky or incomplete metrics mocking in Playwright by routing
traffic through mockttp via Chromium **`--proxy-server`** and
**`--ignore-certificate-errors`** instead of Playwright’s `proxy:`
option, so **MV3 service worker** calls (Segment, Sentry, flags) hit the
mock server like Selenium.
> 
> Tightens **`setParticipateInMetaMetrics`** unit coverage so
**`analyticsId`** stays the same after opt-out **and** opt-back-in, not
only after a single opt-out.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4e6e37f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**


This pull request updates the version of the
`@metamask/bitcoin-wallet-snap` dependency in the `package.json` file to
ensure compatibility with the latest features and bug fixes.

Dependency update:

* Upgraded `@metamask/bitcoin-wallet-snap` from version `^2.0.1` to
`^3.0.0` in `package.json` to use the latest major release.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

Fixes: null

## **Manual testing steps**

1. Interact with the Bitcoin part.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->
N/A

### **After**

<!-- [screenshots/recordings] -->
N/A

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> A major-version Snap upgrade can change Bitcoin signing, account, or
RPC behavior even without local code edits; manual Bitcoin flows and e2e
coverage are the main validation.
> 
> **Overview**
> Upgrades the preinstalled **Bitcoin wallet Snap** by bumping
`@metamask/bitcoin-wallet-snap` from **^2.0.1** to **^3.0.0** in
`package.json`, with matching lockfile resolution to **3.0.0**.
> 
> There are **no extension source changes**—only the bundled Snap
version and checksum update. Bitcoin behavior in the wallet now comes
from the new major Snap release (features/fixes live in that package).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a6ea825. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

QA reported the `Best Rate` tag appearing on provider rows whose
displayed quote was not the cheapest option
([TRAM-3929](https://consensyssoftware.atlassian.net/browse/TRAM-3929)).

Investigation of the reported screenshots showed the backend tags the
highest-`amountOut` quote correctly, but the extension derived provider
tags from **all** of a provider's quotes while displaying only one (the
first non-custom-action quote for the selected payment method). A tag
earned by one quote could therefore render next to a different quote's
amount.

This change derives the tag from the matched (displayed) quote only,
matching mobile's `ProviderSelection` behavior, so a tag can never
appear next to a different quote's amount.

It also vertically centers the tag pill with the provider label: the
design-system `Tag` ships with `self-start` in its base classes, which
overrode the row's `align-items: center` and pinned the pill slightly
high. `RampsProviderListItem` now passes `self-center` (tailwind-merge
resolves the conflict in favor of the consumer class). A Storybook story
was added for the component (Best Rate / Most Reliable / No Tag /
composed rows).

Additionally, provider rows now render **all** matching tags instead of
only the highest-precedence one (previously-used > most reliable > best
rate), in mobile's Quote pill order — a quote that is both most reliable
and best rate now shows both pills.

## **Changelog**

CHANGELOG entry: Fixed a bug that could show the `Best rate` label on a
provider whose displayed quote was not the best rate.

## **Related issues**

[TRAM-3929](https://consensyssoftware.atlassian.net/browse/TRAM-3929)

## **Manual testing steps**

1. Run the extension and open the Buy flow.
2. Enter an amount, select a payment method, and open the "Choose
provider" modal.
3. Verify the `Best rate` tag appears only on the provider row with the
highest crypto amount, and no other row carries the tag.
4. Verify the tag pill is vertically centered with the provider name
(not pinned slightly high).
4. When the top provider carries both backend tags (happens when the
price leader is also the reliability leader), verify both `Most
reliable` and `Best rate` pills render.
5. Verify rows without quotes (listed under "Other options") show no
tag.


## **Screenshots/Recordings**

### **After**

<img width="1280" height="800" alt="provider-selection-best-rate"
src="https://github.com/user-attachments/assets/de68f1b6-464d-4d68-bf38-c6a135c7c77b"
/>

<img width="1280" height="800" alt="both-tags-modal"
src="https://github.com/user-attachments/assets/bd09a4b6-aca2-4756-9256-663c6dc76ab1"
/>

> [!NOTE]
> The first screenshot was taken before the alignment work 

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.


[TRAM-3929]:
https://consensyssoftware.atlassian.net/browse/TRAM-3929?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TRAM-3929]:
https://consensyssoftware.atlassian.net/browse/TRAM-3929?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes buy-flow provider labeling logic that influences which
provider users perceive as best rate; scoped to ramps UI with tests but
affects purchase decision UX.
> 
> **Overview**
> Fixes **Best rate** (and related) labels showing on provider rows
whose **displayed** quote did not earn that tag. Tag metadata is now
taken only from the **matched quote** for the selected payment method
(`getProviderTags` + `matchedQuote`), aligned with mobile, instead of
scanning all quotes for that provider.
> 
> Provider rows now show **all** applicable pills (previously used, most
reliable, best rate) in mobile order, not just the single
highest-precedence tag. **`RampsProviderListItem`** accepts a `tags`
array, vertically centers tag pills (`self-center`), and lets the
provider name truncate beside tags (`min-w-0 flex-1` + name test id).
> 
> Adds Storybook coverage, unit tests for tag derivation and layout, and
snapshot updates.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0400ad4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…t quote dialog (#45997)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

This adds an ETH → ETH Robinhood. After quotes load, the test opens
`More quotes` dialog, asserts each row’s Total cost checks
cheapest-first order, then Back so the selected quote is unchanged.

Here's a video of the test execution:




https://github.com/user-attachments/assets/62d53a73-b51f-454f-8b11-59d6434b6686




## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

1. Open Swap from the homepage.
2. Set destination network to Robinhood and dest token to ETH.
3. Enter 1 ETH and wait for quotes.
4. Open More quotes and confirm Total cost values and cheapest-first
order.
5. Press Back and confirm the original quote is still selected.
6. Submit and confirm activity shows Bridged ETH, Ethereum → Robinhood
Chain.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] 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.



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are mostly E2E mocks, page objects, and a test id on bridge
quote UI; no bridge transaction or auth logic in production paths.
> 
> **Overview**
> Adds **Robinhood chain (4663)** to bridge E2E fixtures—feature flags,
token search/popular mocks, privacy allowlist RPC hosts, and a new
**ETH→ETH Robinhood** quote stream mock—replacing the prior
**Ethereum→Linea** ETH bridge step in the positive-case suite.
> 
> Introduces **`getExpectedQuoteTotalCosts`**, which uses
`@metamask/bridge-controller`’s `calcQuoteMetadata` plus shared
**`MOCK_SUGGESTED_GAS_FEES`** and spot-price helpers so tests can assert
**Select quote** dialog **Total cost** rows (fiat or native fallback) in
**cheapest-first** order without hard-coded amounts. The
**`bridgeTransaction`** flow and quote page object open **More quotes**,
wait for streamed quotes, check each cost, then **Back** without
changing the selected quote.
> 
> Adds **`data-testid="bridge-quote-total-cost"`** on the quotes modal
Total cost label for stable E2E selectors (snapshot updates only).
Network fee checking on the prepare page now waits for a
**`$`**-prefixed fee instead of regex-validating arbitrary text.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
56e70ed. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Version Bump After Release

This PR bumps the main branch version from 13.50.0 to 13.51.0 after
cutting the release branch.

### Why this is needed:
- **Nightly builds**: Each nightly build needs to be one minor version
ahead of the current release candidate
- **Version conflicts**: Prevents conflicts between nightlies and
release candidates
- **Platform alignment**: Maintains version alignment between MetaMask
mobile and extension
- **Update systems**: Ensures nightlies are accepted by app stores and
browser update systems

### What changed:
- Version bumped from `13.50.0` to `13.51.0`
- Platform: `extension`
- Files updated by `set-semvar-version.sh` script

### Next steps:
This PR should be **manually reviewed and merged by the release
manager** to maintain proper version flow.

### Related:
- Release version: 13.50.0
- Release branch: release/13.50.0
- Platform: extension
- Test mode: false

---
*This PR was automatically created by the
`create-platform-release-pr.sh` script.*

Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
## **Description**

This is part of #30394 (removing the clipboardWrite permission). The
permission was used in a few old Components that are no longer in use.

Removed:
- AccountDetails + unit tests + storybook + page object + E2E tests
- AccountDetailsAuthenticate
- AccountDetailsDisplay + unit tests
- AccountDetailsKey
- AccountDetailsSection
- AccountShowPrivateKeyRow + unit tests + storybook
- direct dependency on `@lavamoat/lavadome-core` (it's still around as
an indirect)
- unused strings in `messages.json`, but kept `unexpectedError` in
`messageExceptions`
- `window.logState()` (it used the clipboard, and no consumer was using
it anyway)
- changed `docs/state_dump.md` (it referenced `logState()`, which no
longer exists, but the previous instructions didn't work anyway)
- removed deleted folders from CODEOWNERS
- updated `files-to-convert.json`

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Progresses: #30394

<!--## **Manual testing steps**
## **Screenshots/Recordings**
## **Pre-merge author checklist**
## **Pre-merge reviewer checklist**-->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Removes user-facing export/reveal flows from deleted components; risk
is mainly regression if anything still routed to the old modal, plus
broad locale and dependency churn in security-sensitive areas.
> 
> **Overview**
> Removes **unused legacy account UI** tied to dropping the
`clipboardWrite` permission (#30394): the multichain `AccountDetails`
modal stack (display, authenticate, key reveal, section),
`AccountShowPrivateKeyRow`, related unit/storybook tests, and E2E
coverage (`AccountDetailsModal`, account-type-change spec). Multichain
account private-key E2E now asserts copy via `PrivateKeyModal`.
> 
> **Cleanup elsewhere:** drops the direct `@lavamoat/lavadome-core`
dependency and its type stub; removes `window.logState()`
(clipboard-based) while keeping `logStateString`; updates
`docs/state_dump.md` to **Settings → Privacy → Download state logs**;
prunes obsolete locale keys across locales; adjusts CODEOWNERS,
migration dashboard entries, locale verification (`unexpectedError`
exception), and jest/console baselines.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ff9458a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
release: sync stable to main for version 13.49.0
## **Description**

Updated the deprecated modal content component to use the 32px border
radius specified by the current design direction. This keeps the modal
presentation aligned with the intended visual treatment instead of
relying on the previous design-system `LG` radius token.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

1. Build and load the extension.
2. Open a flow that displays a modal dialog.
3. Verify the modal dialog has rounded corners with a 32px radius and
that its existing size, padding, and interactions are unchanged.


## **Screenshots/Recordings**

Not applicable: this PR updates a small visual token/class value in an
existing component.

### **Before**

<img width="484" height="522" alt="Screenshot 2026-09-18 at 11 13 38 AM"
src="https://github.com/user-attachments/assets/2cc0b6d6-cd64-4f2a-8006-524d0750ddb0"
/>


### **After**

<img width="452" height="795" alt="Screenshot 2026-09-18 at 11 15 32 AM"
src="https://github.com/user-attachments/assets/79d6e529-de1d-4a90-8318-c31b5f6442a9"
/>
<img width="441" height="297" alt="Screenshot 2026-09-18 at 11 15 36 AM"
src="https://github.com/user-attachments/assets/006c44c9-7a06-4edf-9d84-2e9bd66bf2f8"
/>


## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md).
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.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Cosmetic-only change to modal corner radius; no logic, auth, or data
handling.
> 
> **Overview**
> Updates the legacy **ModalContent** dialog styling so corners match
current design (**32px** radius) instead of the old
**`BorderRadius.LG`** / **`mm-box--rounded-lg`** treatment.
> 
> In `modal-content.tsx`, the dialog **`Box`** drops the
**`borderRadius`** prop and adds the Tailwind class **`rounded-[32px]`**
on **`mm-modal-content__dialog`**, so every screen still using this
deprecated wrapper picks up the new shape without per-modal edits. Jest
snapshots across app, bridge, and confirmation modals were refreshed to
reflect the new class list.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7c97937. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Upgrades design system packages to align with the [v71.0.0
release](https://github.com/MetaMask/metamask-design-system/releases/tag/v71.0.0).

**Packages upgraded:**
- `@metamask/design-system-react`: `^0.44.0` → `^0.45.1`

**Changes included:**
- Updated `AvatarBase` (BadgeNetwork) corner radius to 6px
- Updated `ModalContent` corner radius to 32px
- Updated `TextField` container border radius from 8px to 12px
- Updated snapshots for affected components

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

Feature: Design system upgrade to v71.0.0

  Scenario: Core app functionality is unaffected
    Given I am on the main app screen
    When I navigate through the primary user flows
    Then the UI renders correctly with no visual regressions

  Scenario: Modal and bottom sheet components render correctly
    Given I interact with BottomSheetDialog and ModalContent components
    When I view these components in the app
    Then the corner radii are correctly applied (32px)

  Scenario: TextField components display correctly
    Given I interact with TextField components throughout the app
    When I focus and interact with these fields
    Then the border radius is correctly applied (12px)

## **Screenshots/Recordings**

### **Before**

N/A – dependency-only update

### **After**

N/A – dependency-only update

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Dependency-only design-system bump with snapshot updates; visual token
changes only, no app logic or security-sensitive paths touched.
> 
> **Overview**
> Upgrades **`@metamask/design-system-react`** (`^0.44.0` → `^0.45.1`)
and **`@metamask/design-system-shared`** (`^0.38.0` → `^0.39.0`), with
matching **`yarn.lock`** updates—no local component code changes.
> 
> Regenerated Jest snapshots reflect upstream styling: modal /
bottom-sheet dialogs now use **`rounded-[32px]`** instead of
**`rounded-lg`** (e.g. confirm delete network, ramps provider
selection), and small network-badge / account-picker icon wrappers
switch from **`rounded`** to **`rounded-md`** across bridge and
multichain UI tests.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
537b94b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Token Details used a custom back/options bar plus a separate name row
above the price, which did not match the MMDS subpage header pattern
(avatar, symbol, badges, and optional contract address in the nav row).

This PR replaces that layout with `HeaderSubpage` so identity lives in
the header and the chart/price is the first content below it.

**What changed:**
- Added `AssetPageHeader` using MMDS `HeaderSubpage`: back control,
token avatar with network badge, symbol title, security/stock/inactive
badges, and a copyable truncated contract address for non-native tokens.
- Removed the name row above the balance (`assetDisplayName` /
`asset-name` no longer sit above the chart).
- Kept the existing `AssetOptions` overflow menu as `endAccessory` (no
new header icons).
- Switched the overflow trigger to MMDS `ButtonIcon` so it matches the
header back button, and dropped the extra right padding that made the
more button sit farther from the edge than back.
- Updated the token overview E2E back locator to
`data-testid="asset-page-back-button"`.

## **Changelog**

CHANGELOG entry: Moved token name, avatar, and contract address into the
Token Details page header

## **Related issues**

Fixes:
https://consensyssoftware.atlassian.net/jira/software/c/projects/DSYS/boards/1888?selectedIssue=DSYS-1168

## **Manual testing steps**

1. Run `yarn start` and load the unpacked extension.
2. Unlock a wallet with Ethereum selected and open Token Details for
native ETH (home → asset list → ETH).
3. Confirm the header shows back, ETH avatar with network badge, `ETH`
(and verified badge if applicable), and the overflow menu. Confirm there
is no name row above the price, and no contract address in the header.
4. Open overflow and confirm View in explorer still works.
5. Open Token Details for an ERC-20 (for example USDC). Confirm the
header shows a truncated contract address with copy, and that copy puts
the full address on the clipboard.
6. If you have a stock token or inactive asset, confirm those badges
still appear next to the symbol in the header.
7. Press back and confirm you return to the previous screen.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<img width="661" height="161" alt="Screenshot 2026-09-17 at 8 13 24 AM"
src="https://github.com/user-attachments/assets/5e6bee3b-e046-408f-b340-348011655fc1"
/>

### **After**

<!-- [screenshots/recordings] -->
<img width="658" height="819" alt="Screenshot 2026-09-17 at 9 32 46 AM"
src="https://github.com/user-attachments/assets/43a65e70-b5a0-416d-8ec9-425c67d981e9"
/>


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI layout and design-system component migration on the asset page
only; behavior is covered by new unit tests and an updated E2E locator,
with no auth or transaction logic changes.
> 
> **Overview**
> Token Details now uses a shared **MMDS `HeaderSubpage`** layout
instead of a separate back/options row and a name line above the chart.
> 
> A new **`AssetPageHeader`** puts the back control, token avatar with
network badge, **symbol** title (with security/stock/inactive badges
beside it), and the existing overflow menu in one sticky header. For
ERC-20s, a **truncated, copyable contract address** appears under the
symbol; native assets omit it. The chart/price is the first content
below the header.
> 
> **`AssetOptions`** now uses design-system **`ButtonIcon`** (aligned
with the header back control), with layout tweaks so the menu trigger
sits flush like the back button. E2E back navigation targets
**`data-testid="asset-page-back-button"`**, and unit tests cover the new
header behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5faac8f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

- Migrate Selenium tests in the Settings area to Playwright.
- Implement unsupported PW driver methods, now needed
- Add support for artifact download in a test (ie download wallet state)

For reviewers: the spec files are exactly the same, just flagging pw
tests meaning, the review focus is way smaller than what it initially
seems

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/MMQA-2260

## **Manual testing steps**

1. yarn
2. yarn start:test
3. On another terminal: yarn test:e2e:playwright:chrome
test/e2e/tests/settings/account-token-list.pw.spec.ts


## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->



https://github.com/user-attachments/assets/b9b52929-cf55-4289-a132-3d0adbe8d00a



- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to E2E test infrastructure and spec migration; no
production wallet or auth logic is modified.
> 
> **Overview**
> Migrates **Settings** end-to-end coverage from Mocha/Selenium
(`.spec.ts`) to Playwright (`.pw.spec.ts`), using the same flows and
page objects via `withFixtures` with `driverType:
E2E_DRIVER.PLAYWRIGHT`. Selenium counterparts for About, account/token
balances, backup/export, hide-empty tokens, general settings, search,
default address, native-as-main-balance, and localization are removed in
favor of the new `.pw.spec.ts` files.
> 
> **PlaywrightDriver** gains behavior needed by these specs: per-page
**download** listeners that save exports under
`test-artifacts/downloads`, plus implementations for `waitForUrl`,
window handle switching/closing, `pasteIntoField`, and
`clickElementUsingMouseMove`. **Harness tweaks**: Chrome launches with
`--ignore-certificate-errors` so the mock proxy works for MV3 extension
service workers; Firefox persistent context sets `acceptDownloads:
true`. The backup test stays **Firefox-only** (skipped on `chrome-e2e`),
matching the old Selenium Chrome skip.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
de9690e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…46095)

## **Description**

Chainlist.org proposes RouteMesh RPC URLs such as
`https://lb.routeme.sh/rpc/evm/30` when adding networks (for example
Rootstock Mainnet). Two separate validation problems made that flow
report misleading errors.

**1. False "unknown provider" warning.** RouteMesh hosts are not in the
chainid.network safe-chain list, so the add-network confirmation showed
an "unknown provider" warning on the RPC row. This change treats the
exact HTTPS RouteMesh path `/rpc/evm/<decimalChainId>` as a known
provider only when the embedded chain ID matches the requested EIP-3085
chain ID. Wrong-chain paths and spoofed hostnames still warn.

**2. Misleading "Is your RPC URL correct?" error.** Once RouteMesh's
keyless public tier is exhausted, it answers `eth_chainId` with HTTP 429
and JSON-RPC error code `-32029` ("public rate limit exceeded — sign up
for free at https://routeme.sh to continue"). `jsonRpcRequest` collapsed
that into a generic failure, so the add-network flows told users "Could
not fetch chain ID. Is your RPC URL correct?" even though the URL was
correct — and the provider's own explanation was thrown away.

`jsonRpcRequest` now throws a `JsonRpcRequestError` carrying the
JSON-RPC error code and HTTP status, and `isRpcRateLimitError` lets
callers tell throttling apart from an unreachable or incorrect endpoint.
The add-RPC-URL form, networks form, and add-chain confirmation alerts
surface a rate-limit message instead. Submission is still blocked, since
a throttled endpoint would produce a broken network.

Note that this does not make a rate-limited endpoint usable. The
RouteMesh public tier failing is an upstream issue with what
chainlist.org advertises; this change makes MetaMask report the cause
accurately instead of blaming the user's URL.

## **Changelog**

CHANGELOG entry: Fixed misleading errors when adding a custom network
whose RPC provider is rate limiting requests or uses RouteMesh
(`lb.routeme.sh`).

## **Related issues**

Fixes: WPN-1930
Related: #45797

## **Manual testing steps**

False "unknown provider" warning:

1. Run the extension and open a dapp at https://chainlist.org.
2. Add Rootstock Mainnet (chain ID 30) via Connect Wallet so MetaMask
shows the add-network confirmation.
3. Confirm the RPC field shows `lb.routeme.sh/rpc/evm/30`.
4. Click the RPC warning/info control.
5. Verify the "submitted RPC URL value does not match a known provider
for this chain ID" alert is **not** shown for this URL.
6. Confirm that adding a network with a different host, or a RouteMesh
path for a different chain ID, still shows the unknown-provider warning.

Rate-limit reporting:

7. Go to Settings > Networks > Add a network > Add RPC URL and enter
`https://lb.routeme.sh/rpc/evm/30`.
8. Verify the error reads "This RPC provider is limiting requests. Try
again later or use a different RPC URL." rather than "Could not fetch
chain ID. Is your RPC URL correct?".
9. Verify the Add URL button stays disabled.
10. Replace the URL with a working endpoint such as
`https://public-node.rsk.co` and verify no error appears and the button
becomes enabled.
11. Enter an unreachable URL such as `https://not-an-rpc.example.com`
and verify the original "Could not fetch chain ID. Is your RPC URL
correct?" error still appears.

<!--
## **Screenshots/Recordings**
### **Before**
### **After**
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.
-->

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
…46418)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

This PR stabilizes the Shield crypto annual-plan E2E after picking a
payment token.

**Root cause:**

- The test opens the Shield plan page from the entry modal and chooses
the annual plan.
- It then clicks Pay with crypto to pick USDC.
- The fixture seeds both USDC and USDT, so Pay with crypto opens a
nested asset picker and does not close the Change payment method modal.
- After clicking USDC, the helper returns right away.
- The test then clicks Continue.
- Continue is in the DOM, but the Change payment method overlay still
covers it, so the click cannot reach the button.

**Solution:**

- Wait for the nested asset picker (`asset-picker-modal`) after clicking
Pay with crypto, so the token list is on screen before picking USDC.
- After picking USDC, wait until the Change payment method modal
(`shield-payment-modal`) is gone, using the plan page as a guard, so
Continue is uncovered before the one Continue click.

Slack:
https://consensys.slack.com/archives/CTQAGKY5V/p1789633666997879?thread_ts=1789633473.891219&cid=CTQAGKY5V
## **Changelog**

CHANGELOG entry: null

<!--
## **Related issues**

Fixes:
-->

## **Manual testing steps**

1. Start a test build with `yarn start:test`.
2. Run:

```bash
SELENIUM_BROWSER=chrome yarn test:e2e:single test/e2e/tests/shield/shield-plan-subscription.spec.ts --debug --leave-running
```

3. Confirm the case `should get started on entry modal - annual plan`
picks USDC, waits for the Change payment method modal to close, then
clicks Continue successfully.

## **Screenshots/Recordings**

### **Before**

CI screenshot at failure: Change payment method overlay still open (Pay
with USDC selected, Pay with card unselected). Continue is behind
`mm-modal-content`. The test timed out on `clickContinueButton`.

<img width="780" height="493" alt="image"
src="https://github.com/user-attachments/assets/91e40062-eff2-40e3-860c-f8e88632404a"
/>


### **After**

No product UI change. After picking USDC, the helper waits until the
payment modal is gone, then Continue is clicked once.

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> E2E page-object timing only; no production UI or application logic
changes.
> 
> **Overview**
> Fixes flaky Shield annual-plan E2E where **Continue** was clicked
while the **Change payment method** overlay still blocked it after
choosing USDC.
> 
> `selectCryptoPaymentToken` now **waits for the nested asset picker**
(`asset-picker-modal`) before selecting the token, then **waits until
`shield-payment-modal` is gone** (scoped with the plan page guard) so
the helper only returns once **Continue** is clickable.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
57e5204. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Please submit this PR as a draft initially.
Do not mark this as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Stabilizes the Trezor hardware-wallet E2E that opens Connect hardware
from the account list.

**Root cause:**

- The test logs in, opens the account menu, and lands on the Accounts
screen.
- It waits until the footer no longer says Syncing, then clicks Add
wallet and looks for Hardware wallet.
- Add wallet can look ready while the list is still updating balances
and other accounts.
- The click is recorded as success, but the wallet never leaves
Accounts.
- The next wait for the Hardware wallet option times out on the Accounts
screen.

**Solution:**

- Wait until Add wallet is enabled and not showing Syncing, and stays
that way for 2 seconds, so the click happens after the footer stops
flickering.
- Click Add wallet once after that wait. Hardware wallet, import, and
watch-account flows use the same helper so they do not hit the same
race.
- Account list load uses that same wait instead of a short "Syncing is
gone" check, so the page is not marked loaded while the footer is still
changing.

**Slack:**
https://consensys.slack.com/archives/CTQAGKY5V/p1789723483575219?thread_ts=1789719783.184109&cid=CTQAGKY5V

## **Changelog**

CHANGELOG entry: null

<!--
## **Related issues**

Fixes:
-->

## **Manual testing steps**

1. Start a test build with `yarn start:test` if one is not already
running.
2. Run `SELENIUM_BROWSER=chrome yarn test:e2e:single
test/e2e/tests/hardware-wallets/trezor/trezor-account.spec.ts`.
3. Confirm the test opens Accounts, reaches the hardware wallet picker,
and unlocks the first Trezor account.

## **Screenshots/Recordings**
<img width="1366" height="682" alt="image"
src="https://github.com/user-attachments/assets/8a925f13-7aa8-45d7-a21e-527216284beb"
/>

### **Before**

CI screenshot at failure: still on Accounts. Account 1 and Add wallet
are visible. The Hardware wallet option is missing because Add wallet
never opened the choose-wallet-type page.

### **After**

No product UI change. After Accounts loads, the helper waits until Add
wallet stays enabled, then clicks it once and the Hardware wallet option
is on screen.

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only page object changes; no production UI or wallet logic.
> 
> **Overview**
> Fixes flaky Trezor (and related) E2E flows where **Add wallet** was
clicked while the account list footer was still syncing or flickering,
so navigation to choose-wallet-type never happened.
> 
> The account list page object now **waits until Add wallet is enabled,
not showing Syncing, and stays that way for 2s**
(`waitForAddWalletButtonStablyReady`), then clicks via a shared
**`clickAddWalletButton`** helper. Hardware wallet, import,
watch-account, and other flows that opened Add wallet directly now use
that helper.
> 
> **`waitUntilSyncingIsCompleted`** (used when marking the list loaded
and before add-account) delegates to the same stable-ready wait, bumps
the default timeout from 10s to 15s, and drops the old “Syncing gone +
assert Add wallet visible” sequence.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
801b971. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
…tils to TypeScript (#46430)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
Migrate three `shared/lib` modules and their colocated tests from
JavaScript to TypeScript.
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

Fixes: MetaMask/MetaMask-planning#7405

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches provider injection gating and swaps quote/tx construction
paths; most changes are typing, but guarded address-book updates and
query-string construction could subtly change runtime behavior.
> 
> **Overview**
> Migrates **`provider-injection`**, **`swaps-utils`**, and
**`updateTxData`** (plus colocated tests) from JavaScript to TypeScript
and drops them from the TS migration dashboard list.
> 
> **Provider injection** gains explicit typings and builds the
eligibility URL from `window.location.href` instead of passing
`window.location` directly.
> 
> **`swaps-utils`** is reimplemented as **`swaps-utils.ts`** with typed
API/quote validators and request shapes; trade fetching now builds query
strings via `URLSearchParams` with stringified values (including
`enableDirectWrapping`). Tests drop unused `fetchTradesInfo` arguments
that no longer exist on the typed input.
> 
> **`updateTxData`** exports **`UpdateTxDataMeta`** /
**`UpdateTxDataParams`** tied to `TransactionMeta`, and only calls
**`addToAddressBookIfNew`** when the callback and `toAddress` /
`toAccounts` are all present.
> 
> **`swaps.util.ts`** validator helpers are aligned with the shared
module’s optional `(value: unknown)` validators.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f7d2879. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…es typed address without 0x prefix and sends TST` (#46499)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

The spec is flaky as after confirming the transaction, we try to click
on the Confirmed tx to see the details, but sometimes the tx toast
notifications appears, intercepting the click

<img width="1033" height="302" alt="image"
src="https://github.com/user-attachments/assets/0c205b64-19ae-4d01-b7e9-7e4e14711147"
/>


Now we clickElementSafe, so if the toast is there, we close it

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

1. Check ci

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<img width="780" height="493" alt="image"
src="https://github.com/user-attachments/assets/6245aeca-9857-4062-afd2-0d824f215adb"
/>

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only change with no production code impact.
> 
> **Overview**
> Fixes flakiness in the **ERC20 “normalizes typed address without 0x
prefix and sends TST”** send E2E by dismissing the post-confirmation
transaction toast before opening the confirmed activity entry.
> 
> After the tx is confirmed and the activity list is shown, the test now
uses **`TxToastNotification.closeToastNotification()`** so the overlay
does not block or race with **`clickConfirmedTransaction()`** when
verifying the normalized address in the activity log.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
523c4ad. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Added Sentry performance traces for the unified Swaps/Bridge experience
so the extension reports the same key dashboard signals as Mobile. The
traces cover bridge view loading, quote fetching, quote stream
completion, balances, token selection, and relevant entry-point/request
metadata.

The implementation also carries the view trace across bridge navigation
and avoids treating React StrictMode's development-only effect probe as
a cancelled page visit.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

SWAPS-5033: https://consensyssoftware.atlassian.net/browse/SWAPS-5033

## **Manual testing steps**

1. Run the extension with `yarn webpack --watch` and load the unpacked
build in Chrome.
2. Open the unified Swaps/Bridge page from the extension homepage and
from a token detail page; also open it through a bridge deep link when
available.
3. Complete a normal quote request, refresh quotes, and exercise an
unsuccessful/no-quote request.
4. In the extension service-worker console, verify the relevant trace
lifecycle logs, then confirm the corresponding traces and result
attributes appear in the MetaMask Sentry dashboard.

<!--
## **Screenshots/Recordings**

Not applicable: this change adds telemetry and does not change the
rendered UI.
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md)
and [MetaMask Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md).
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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Telemetry-only changes with no user-facing swap logic; minor risk of
misleading metrics if lifecycle edge cases (navigation, debounce,
StrictMode) are wrong.
> 
> **Overview**
> Adds **Sentry performance tracing** for the unified Swaps/Bridge UI so
extension dashboards can align with Mobile: view load, quote fetch,
balances, and token API latency.
> 
> **Page load (`Swap View Loaded`)** now starts on entry (navigation
hook or deep link), carries a trace id through router state, and ends
when tokens, source balance, and (if prefilled) the quote surface are
ready—or is marked **cancelled** on real unmount (StrictMode-safe).
> 
> **Quote fetch** moves to a shared `swapQuoteFetchTrace` helper:
debounced starts cancel superseded requests; completion uses
**success**, **no_quotes** (with stream reason), **error**, or
**cancelled**, and ignores stale quotes when the asset pair changed.
> 
> Other **cache-miss** fetches emit **`bridge.data_fetch`** traces for
popular tokens and token search; source balance reads use explicit
start/end with **success/error**. **`Swap Quotes Fetched`** tags the
legacy background quote thunk with the same operation.
> 
> New **`docs/swaps/swaps-sentry-reference.md`** documents trace names,
ops, panel titles, and example Sentry filters. Tests and navigation
expectations were updated for trace ids and entry-point metadata.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5516a52. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…46338)

## **Description**

Hardware wallet accounts cannot sign the batch that executes a Money
Account deposit or withdrawal, but the extension still offered them in
two places:

1. **The account picker.** The "From" row on the Money Account
confirmation listed every EVM account, filtered only by
`isEvmAccountType`. A user could select a Ledger and only then hit a
blocking alert — a dead end, since the account was offered and then
rejected.
2. **The entry point.** `useMoneyAccountDeposit` /
`useMoneyAccountWithdrawal` seeded `txParams.from` from the globally
selected account, checking only that it was EVM. A user on a hardware
wallet who tapped "Add funds" landed on the confirmation already
blocked, having never made a choice.

This PR closes both paths:

- Hardware accounts are **hidden from the account picker** for flows
that forbid them (Money Account deposit/withdraw, Perps, Predict).
- The funding/recipient account is resolved by a new
`selectMoneyFundingAccount`, which prefers the globally selected account
and **falls back to the user's first eligible EVM account** when it is
ineligible. Initiation now fails only when the user has no eligible
account at all.

The blocked transaction-type lists moved from a private list inside the
alert hook into `constants/pay.ts`, behind a shared
`useIsPayHardwareBlocked` hook. The picker and the alert now read the
same logic, so the list a user can choose from and the alert that blocks
them cannot drift apart.

**Notes for reviewers:**

- `usePayHardwareAccountAlert` is intentionally **kept**, not deleted as
redundant. It still guards addresses the picker never vetted — deep
links and any other entry point that sets `txParams.from` directly. It
is now documented as a backstop.
- The fallback reads `internalAccounts.accounts`, which
`AccountsController` rebuilds in keyring order, so "first" matches what
the rest of the UI treats as first. It deliberately avoids the
balance-joined ordered-accounts selector, which would pull keyring and
balance state into entry points that only need an address.
- The Money Account itself can never be selected as its own funding
source: `AccountsController` excludes the Money keyring from
`internalAccounts` entirely (it is owned by `MoneyAccountController`).
- The fallback is **silent** — the user is not told their funding
account was substituted. The From row does display it and it remains
editable. Happy to add an explicit toast/inline note if product prefers.

## **Changelog**

CHANGELOG entry: Fixed an issue where a hardware wallet account could be
selected for a Money Account deposit or withdrawal, which is not
supported

## **Related issues**

Fixes:
[CONF-1980](https://consensyssoftware.atlassian.net/browse/CONF-1980)

## **Manual testing steps**

Requires a wallet with at least one hardware wallet account
(Ledger/Trezor) and at least one non-hardware EVM account, with the
Money Account feature enabled.

**Account picker no longer lists hardware accounts**

1. Select any non-hardware account.
2. Go to the Money home page and tap **Add** to start a deposit.
3. On the amount screen, tap the **From** account pill to open the
account picker.
4. Verify no hardware wallet accounts are listed.
5. Verify a wallet whose accounts are *all* hardware does not appear as
an empty section header.
6. Verify non-hardware accounts (HD and imported) are still listed and
selectable.
7. Repeat steps 2–6 for **Send → Withdraw** to confirm the withdraw flow
behaves the same.

**Hardware account is not used to create the transaction**

8. Switch the globally selected account to a **hardware wallet**
account.
9. From the Money home page, tap **Add**.
10. Verify the confirmation opens normally with **no blocking "Wallet
not supported" alert**.
11. Verify the **From** row shows a non-hardware account (the first
eligible one), not the selected hardware account.
12. Repeat steps 9–11 for the withdraw flow, confirming the destination
account is the non-hardware account.

**Unrelated flows are unchanged**

13. With a hardware account selected, start a regular send or a contract
interaction.
14. Verify the account picker still lists hardware accounts and the flow
is unaffected.

<!--
## **Screenshots/Recordings**

### **Before**

### **After**
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.


[CONF-1980]:
https://consensyssoftware.atlassian.net/browse/CONF-1980?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes which account funds or receives Money flows and silently
substitutes when hardware is selected; mistakes could send funds from/to
the wrong address, though behavior is covered by new selector and hook
tests.
> 
> **Overview**
> Hardware wallets cannot sign Money Account deposit/withdraw batches,
but the extension still let users pick them or land on a blocked
confirmation when a Ledger/Trezor was globally selected.
> 
> **Entry points** now resolve funding/recipient via
**`selectMoneyFundingAccount`**: use the selected account when it is an
eligible non-hardware EVM account, otherwise **fall back to the first
eligible EVM account** in wallet order. Deposit and withdraw hooks only
fail when no eligible account exists.
> 
> **Confirmations** hide hardware accounts in the **From/To account
picker** for Pay flows that forbid them (Money deposit/withdraw, Perps,
Predict, and flag-gated mUSD conversion).
`getEvmAccountsGroupedByWallet` and `AccountSelectModal` gain an
**`excludeHardwareAccounts`** option; `FromAccountRow` drives it from a
new shared **`useIsPayHardwareBlocked`** hook. Blocked transaction-type
lists live in **`constants/pay.ts`** so the picker and
**`usePayHardwareAccountAlert`** (kept as a backstop for deep links and
pre-seeded `txParams.from`) stay aligned.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f63a791. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
… info (#46507)

## **Description**

Small polish to the ramps provider selection screen:

- The provider selection modal heading now reads "Choose providers" (was
"Choose provider") to match the plural list it introduces.
- The "Most reliable" provider tag now renders with `Info` severity
(blue) instead of `Neutral` (gray); "Best rate" remains `Success`
(green).
- Synced the `rampsChooseProvider` string in `en_GB` with `en`.


## **Changelog**

CHANGELOG entry: Updated the buy flow's provider selection screen to
read "Choose providers" and to show the "Most reliable" tag in blue.

## **Related issues**

<!--
No Jira ticket id is derivable from the branch name (`jira-check`).
-->

Fixes:
[TRAM-3967](https://consensyssoftware.atlassian.net/browse/TRAM-3967?atlOrigin=eyJpIjoiZDViZjBiNGM0NDVjNGY0MWJhNzNiMjU4NDk3NzZiNmQiLCJwIjoiaiJ9)

## **Manual testing steps**

1. Build the extension and load it in Chrome.
2. Onboard a wallet (import or create).
3. From the wallet home, open the buy flow and select ETH on Ethereum.
4. On the build-quote screen, click "change providers".
5. Verify the modal heading reads "Choose providers".
6. Verify the "Most reliable" tag pill renders blue and the "Best rate"
tag renders green.

## **Screenshots/Recordings**

### **After**

<img width="1280" height="800"
alt="03-choose-providers-modal-1789980333869"
src="https://github.com/user-attachments/assets/2586100d-1679-4aab-bb04-bee952ce9e8e"
/>



## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Code Review
Guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've confirmed code meets the repo's coding standards.
- [ ] I've tested the changes per the manual testing steps.


[TRAM-3967]:
https://consensyssoftware.atlassian.net/browse/TRAM-3967?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Copy, styling, and tag severity only in the ramps buy
provider-selection UI; no auth, payments, or backend logic changes.
> 
> **Overview**
> Polishes the ramps **provider selection** modal copy and tag styling.
> 
> The modal title string `rampsChooseProvider` is updated to **"Choose
providers"** in `en` and `en_GB`, and the dialog gets an explicit
**`rounded-[32px]`** class on modal content (snapshots reflect the
heading and class reorder).
> 
> **Most reliable** provider pills now use **`TagSeverity.Info`** (blue)
instead of **Neutral**, aligning with **Previously used**; **Best rate**
stays **Success** (green). Tests, stories, and snapshots are updated for
the new copy and severities.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3d7cc48. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Several Core Extension UX actions still override the shared pill-shaped
button default with rounded-square corners.

This removes those overrides from multichain import/network actions and
toast actions, and updates critical-error recovery buttons to use
pill-shaped corners.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Open the affected import, network, toast, and critical-error
surfaces.
2. Verify their text/action buttons have pill-shaped corners in their
normal state.
3. Verify disabled, hover, pressed, and loading states retain their
existing visual behavior.

## **Screenshots/Recordings**

Before/after Storybook screenshots will be added as inline PR comments
where stories are available; temporary sandbox stories will cover other
components.

### **Before**

### **After**

## **Pre-merge author checklist**

- [ ] I've followed MetaMask Contributor Docs and Extension Coding
Standards.
- [ ] I've completed the PR template to the best of my ability.
- [ ] I’ve included tests if applicable.
- [ ] I’ve documented code using JSDoc if applicable.
- [ ] I’ve applied the right labels.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR.
- [ ] I confirm that this PR includes the necessary testing evidence.

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…46508)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## **Description**

When viewing a native asset on a different network than the currently
selected one (e.g., viewing ETH on Mainnet while connected to a custom
network), clicking "View on explorer" was incorrectly opening the custom
network's block explorer instead of Etherscan.

The root cause was that `getRpcPrefsForCurrentProvider` returned the
`rpcPrefs` for the **currently selected network**, not the network of
the asset being viewed. When the user had a custom network selected, its
`blockExplorerUrl` was passed to `getAccountLink`, overriding the
correct explorer for the asset's chain.

This fix looks up the network configuration for the asset's specific
`chainId` using `selectNetworkConfigurationByChainId` and extracts the
`blockExplorerUrl` from that configuration instead of using the current
provider's rpcPrefs.

## **Changelog**

CHANGELOG entry: Fixed a bug where clicking "View on explorer" for a
token would open the wrong network's block explorer

## **Related issues**

Fixes: #46372

## **Manual testing steps**

1. Add any new custom network to MetaMask (with a custom block explorer
URL)
2. Switch to or remain on Ethereum Mainnet
3. Navigate to the ETH native asset page
4. Click the 3-dot menu → "View on explorer"
5. Verify that Etherscan opens (not the custom network's explorer)
6. Switch to the custom network and view its native asset
7. Click the 3-dot menu → "View on explorer"
8. Verify that the custom network's explorer opens correctly

<!--
## **Screenshots/Recordings**

### **Before**

### **After**
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-a6cf49a0-230c-4eba-8c6f-bffa5cbcdeef?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-a6cf49a0-230c-4eba-8c6f-bffa5cbcdeef&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Amélie <ameliejyc@users.noreply.github.com>
## **Description**

Split from #45897. Moves ref writes out of render and fixes
`react-hooks/exhaustive-deps` in multichain, compliance, and misc pages
for MetaMask-planning#6402.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: MetaMask/MetaMask-planning#6402

## **Manual testing steps**

<!--
## **Screenshots/Recordings**
### **Before**
### **After**
-->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.
-->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Refactors compliance gating, seed reveal, and mUSD tracing lifecycle
with hook-timing changes; intent is parity but these paths are security-
or metrics-sensitive.
> 
> **Overview**
> Clears a batch of **`react-hooks/refs`** (and related) suppressions by
stopping ref writes and ad-hoc render updates across multichain menus,
compliance, subscriptions, and several settings/confirm flows.
> 
> **Popover/menu anchoring** now uses callback refs plus state
(`referenceElement` / `anchorElement`) so `Menu`/`Popover` get a DOM
node after commit—touched in account menus, permission/connected-account
options, dropdown editor, and network menu stories.
**`useComplianceGate`** moves `currentAddressKeyRef` and
`checkComplianceRef` updates into **`useLayoutEffect`** instead of
during render.
> 
> **Other behavior-preserving refactors:** confirm **expandable row**
height via measured state; **`useNetworkManager`** reads URL `view` in
`handleClose` deps instead of a ref; **`useMusdConversionConfirmTrace`**
tracks active traces with state (plus a test when `transactionId` clears
after confirm); **`reveal-seed`** and **QR sync scan** replace
render-time ref/state hacks with explicit state/effects;
**`useSubscriptionError`** syncs the error ref in layout effect.
**`eslint-suppressions.json`** drops the fixed file entries, and
**multichain account menu** tests **`waitFor`** the menu button after
the ref callback change.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3d130f4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

The wallet creation success page settings action still applies a
rounded-square radius.

This removes the explicit radius so the action inherits the shared
pill-shaped button style.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Complete wallet creation and open the creation-successful page.
2. Verify the Manage default settings action has pill-shaped corners.
3. Verify the settings icon, label alignment, navigation, and responsive
width remain unchanged.

## **Screenshots/Recordings**

Before/after Storybook screenshots will be added as inline PR comments.

### **Before**

### **After**

## **Pre-merge author checklist**

- [ ] I've followed MetaMask Contributor Docs and Extension Coding
Standards.
- [ ] I've completed the PR template to the best of my ability.
- [ ] I’ve included tests if applicable.
- [ ] I’ve documented code using JSDoc if applicable.
- [ ] I’ve applied the right labels.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR.
- [ ] I confirm that this PR includes the necessary testing evidence.

Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Thinner alternative to #46510 for MUSD-1412.

Duplicate deposit toasts happen because Pay source legs fire
`transactionStatusUpdated` before Redux has the parent's
`requiredTransactionIds`. The generic listener then shows "Transaction
submitted" next to the Money deposit toast.

This keeps `useTransactionEventToasts` generic. The only change there is
one extra skip:

`isKnownMoneyBatchChild(id)`

The money toast listener fills a small UI registry from messenger
payloads (`requiredTransactionIds`), so the generic listener can exclude
those children without waiting on Redux and without deferring unrelated
toasts.

No pending/confirm/fail branching, no `store.subscribe`, no timeout in
the generic listener.

## **Changelog**

CHANGELOG entry: Fixed duplicate notifications shown during Money
Account deposits

## **Related issues**

Fixes:
[MUSD-1412](https://consensyssoftware.atlassian.net/browse/MUSD-1412)
[MUSD-1413](https://consensyssoftware.atlassian.net/browse/MUSD-1413)

## **Manual testing steps**

1. Open Money Account and start a deposit.
2. Pay with ETH on Arbitrum (the known-repro route).
3. Confirm the deposit. Verify only the Money deposit/convert toast
appears — not a second "Transaction submitted" toast.
4. Confirm a normal send still shows "Transaction submitted"
immediately.

## **Screenshots/Recordings**

### **Before**

<img width="752" height="370" alt="Screenshot 2026-09-18 at 16 29 40"
src="https://github.com/user-attachments/assets/1462fa3d-cff4-48c2-8d68-9f4f885aa0e1"
/>

### **After**

<img width="791" height="662" alt="Screenshot 2026-09-21 at 13 35 57"
src="https://github.com/user-attachments/assets/ea527680-9430-4d8b-944a-2dc62a301381"
/>

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
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.

[MUSD-1412]:
https://consensyssoftware.atlassian.net/browse/MUSD-1412?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only toast filtering with a small module-scoped registry; behavior
is covered by new unit tests and does not touch auth, signing, or
transaction submission.
> 
> **Overview**
> Fixes duplicate deposit notifications by tracking Money batch **child
transaction IDs** from messenger payloads before Redux catches up.
> 
> Adds an in-memory **`money-batch-registry`** that records each Money
deposit batch’s `requiredTransactionIds`. The Money account toast
listener **registers** the batch on pending lifecycle events and
**clears** it on confirm or failure (including when no success toast is
shown).
> 
> The generic transaction toast hook now **bails out early** when
`isKnownMoneyBatchChild(id)` is true, so Pay/funding legs (e.g. relay
deposits) no longer trigger a second “Transaction submitted” toast
alongside the Money deposit toast. Unrelated relay deposits and normal
sends are unchanged.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b39d4d0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com>
## **Description**

The money account balance Add action still applies a rounded-square
radius despite using the shared button component.

This removes the explicit radius so the action inherits the pill-shaped
button default.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Open the money account balance surface with the Add action visible.
2. Verify the Add action has pill-shaped corners.
3. Verify loading, disabled, and balance-state transitions remain
unchanged.

## **Screenshots/Recordings**

Before/after Storybook screenshots will be added as inline PR comments
where a story is available; a temporary sandbox story will cover this
component if needed.

### **Before**

### **After**

<img width="400" alt="After"
src="https://raw.githubusercontent.com/MetaMask/metamask-extension/screenshots/visual-regression-extension/pr-46475/after/earn-money-account-balance.png"
/>

## **Pre-merge author checklist**

- [x] I've followed MetaMask Contributor Docs and Extension Coding
Standards.
- [x] I've completed the PR template to the best of my ability.
- [x] I’ve included tests if applicable.
- [x] I’ve documented code using JSDoc if applicable.
- [x] I’ve applied the right labels.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR.
- [ ] I confirm that this PR includes the necessary testing evidence.

Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
@metamask-ci

metamask-ci Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
Builds ready [53752b4] [reused from 674c2d0]
⚡ Performance Benchmarks (Total: 🟢 7 pass · 🟡 4 warn · 🔴 2 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 36803295618 | Baseline logs

Metricschrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 load_new_account(p95) [CI log]🔴 load_new_account(p95) [CI log]

Regressions (🔴 2 failures)

Interaction Benchmarks · Samples: 5 🔴 2
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 [CI log]
🔴 load_new_account
🔴 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ loadNewAccount/load_new_account: +418%
  • ↑ loadNewAccount/total: +418%
  • ↑ confirmTx/longTaskTotalDuration: +42%
  • ↑ confirmTx/longTaskMaxDuration: +68%
  • ↑ confirmTx/tbt: +73%
  • ↑ confirmTx/lcp: +15%
  • ↑ bridgeUserActions/bridge_load_page: +49%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +69%
  • ↑ bridgeUserActions/longTaskCount: +67%
  • ↑ bridgeUserActions/longTaskTotalDuration: +48%
  • ↑ bridgeUserActions/tbt: +25%
  • ↑ bridgeUserActions/total: +10%
  • ↑ loadNewAccount/load_new_account: +422%
  • ↑ loadNewAccount/total: +422%
  • ↓ loadNewAccount/fcp: -43%
  • ↑ loadNewAccount/lcp: +1511%
  • ↓ confirmTx/longTaskCount: -100%
  • ↓ confirmTx/longTaskTotalDuration: -100%
  • ↓ confirmTx/longTaskMaxDuration: -100%
  • ↓ confirmTx/tbt: -100%
  • ↓ confirmTx/inp: -17%
  • ↓ confirmTx/fcp: -42%
  • ↑ confirmTx/lcp: +1229%
  • ↑ bridgeUserActions/bridge_load_page: +274%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +84%
  • ↑ bridgeUserActions/bridge_search_token: +19%
  • ↓ bridgeUserActions/longTaskCount: -100%
  • ↓ bridgeUserActions/longTaskTotalDuration: -100%
  • ↓ bridgeUserActions/longTaskMaxDuration: -100%
  • ↓ bridgeUserActions/tbt: -100%
  • ↑ bridgeUserActions/total: +55%
  • ↑ bridgeUserActions/inp: +23%
  • ↓ bridgeUserActions/fcp: -56%
  • ↑ bridgeUserActions/lcp: +1256%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 1.8s
  • 🟡 confirmTx/FCP: p75 1.9s
  • 🟡 bridgeUserActions/FCP: p75 1.9s
  • 🟡 loadNewAccount/LCP: p75 2.8s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 loadScripts

📈 Results compared to the previous 5 runs on main

  • ↓ startupStandardHome/firstReactRender: -99%
  • ↓ startupStandardHome/initialActions: -33%
  • ↑ startupStandardHome/setupStore: +14%
  • ↑ startupStandardHome/numNetworkReqs: +10%
  • ↓ startupStandardHome/longTaskCount: -33%
  • ↓ startupStandardHome/longTaskTotalDuration: -14%
  • ↓ startupStandardHome/tbt: -10%
  • ↓ startupStandardHome/domInteractive: -32%
  • ↓ startupStandardHome/firstReactRender: -98%
  • ↓ startupStandardHome/initialActions: -50%
  • ↑ startupStandardHome/setupStore: +163%
  • ↓ startupStandardHome/fcp: -26%
User Journey Benchmarks · Samples: 5 · real API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 total
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ onboardingImportWallet/srpButtonToSrpForm: +41%
  • ↑ onboardingImportWallet/confirmSrpToPwForm: +49%
  • ↑ onboardingImportWallet/pwFormToMetricsScreen: +73%
  • ↑ onboardingImportWallet/metricsToWalletReadyScreen: +30%
  • ↓ onboardingImportWallet/doneButtonToHomeScreen: -94%
  • ↓ onboardingImportWallet/openAccountMenuToAccountListLoaded: -75%
  • ↓ onboardingImportWallet/longTaskCount: -100%
  • ↓ onboardingImportWallet/longTaskTotalDuration: -100%
  • ↓ onboardingImportWallet/longTaskMaxDuration: -100%
  • ↓ onboardingImportWallet/tbt: -100%
  • ↓ onboardingImportWallet/total: -89%
  • ↑ onboardingNewWallet/srpButtonToPwForm: +31%
  • ↑ onboardingNewWallet/createPwToRecoveryScreen: +23%
  • ↑ onboardingNewWallet/skipBackupToMetricsScreen: +22%
  • ↑ onboardingNewWallet/agreeButtonToOnboardingSuccess: +16%
  • ↓ onboardingNewWallet/doneButtonToAssetList: -81%
  • ↓ onboardingNewWallet/longTaskCount: -69%
  • ↓ onboardingNewWallet/longTaskTotalDuration: -79%
  • ↓ onboardingNewWallet/longTaskMaxDuration: -50%
  • ↓ onboardingNewWallet/tbt: -98%
  • ↓ onboardingNewWallet/total: -78%
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↓ dappPageLoad/pageLoadTime: -56%
  • ↓ dappPageLoad/firstPaint: -41%
  • ↓ dappPageLoad/firstContentfulPaint: -41%
Bundle Size Diffs [🚨 Warning! Bundle size has increased!]
Status Bundle Total Diff Change
✅ background 15.49 MiB +631 Bytes 0.00%
🚨 ui 19.01 MiB +11.78 KiB +0.06%
✅ common 0 Bytes 0 Bytes 0.00%
✅ other 1.98 MiB 0 Bytes 0.00%
✅ content scripts 1.97 MiB +12 Bytes 0.00%
✅ zip 23.57 MiB -35.9 KiB -0.15%
### 🍒 What's in this RC

Cherry-picks (10 commits)
Commit Description
674c2d0f2b release(runway): cherry-pick chore: audit webpack-dev-middleware and undici for security advisories (#46827)
c3457acf2f release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46824)
08d1af6a46 release: release-changelog/13.51.0 (#46635)
d0ef1938e0 Update Attributions
cc0f89183b release(runway): cherry-pick fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components (#46764)
03039f7afa release(runway): cherry-pick fix(money): remove Paid by MetaMask from transaction fee tooltips (#46746)
601998bb20 release(runway): cherry-pick feat: ensure parity with mobile money account events tracking (#46752)
b138b9aa2f release(runway): cherry-pick fix(confirmations): gate money account perps pay on its feature flag (#46709)
11ef94709f Merge pull request #46693 from MetaMask/stable-sync-release-13.51.0
fcb4edcf43 Merge branch 'release/13.51.0' into stable-sync-release-13.51.0

Changelog (127 commits since v13.50.0)
Commit Description
674c2d0f2b release(runway): cherry-pick chore: audit webpack-dev-middleware and undici for security advisories (#46827)
c3457acf2f release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46824)
08d1af6a46 release: release-changelog/13.51.0 (#46635)
d0ef1938e0 Update Attributions
cc0f89183b release(runway): cherry-pick fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components (#46764)
03039f7afa release(runway): cherry-pick fix(money): remove Paid by MetaMask from transaction fee tooltips (#46746)
601998bb20 release(runway): cherry-pick feat: ensure parity with mobile money account events tracking (#46752)
b138b9aa2f release(runway): cherry-pick fix(confirmations): gate money account perps pay on its feature flag (#46709)
e048b5070e release(runway): cherry-pick fix: Fix migration crash (#46683)
fbb2ecd9f0 release(runway): cherry-pick fix: update Basic Functionality privacy messaging (#46640)
c8e80f78e7 Merge release/13.50.0 into release/13.51.0
eac5d1074d refactor(bundle-size): polish pr comment layout (#41912)
109504d7c4 fix(ramps): explain quote unavailability in pay-with warning tooltip (#46505)
1729f4bb5c feat(clipboardWrite): remove the clipboardWrite permission from the manifest file (#46603)
3e6f28dc69 fix(client-mcp-core): update to 0.10.0 (#46596)
8f5a593df3 fix(ramps): validate quote buy URL before fetching the buy widget (#46504)
117a8f3bc1 feat: tag scam questionnaire support visits (#46590)
f43cc73e73 test: ignore console error in Tron account derivation (#46624)
f0e645dc4b fix: handle resolved scam questionnaire domains (#46568)
b460c61144 fix: MUSD-1385 use the up-right arrow icon for Sent money activity rows (#46457)
6b0c405ac2 chore: improve spacing between elements on money home (#46619)
2eaf1af858 feat(nfts): show basic functionality off empty state in NftsTab and update the links cp-13.50.0 (#46557)
b11264e482 chore: New Crowdin Translations by GitHub Action cp-13.50.0 (#46512)
16e8dadf57 chore(activity): remove client side from/to filter (#46605)
b7a1e813c4 refactor(settings): proper settings index route (#46609)
81a9d511dc refactor(7371): convert migrations 027–053 to TypeScript (#46604)
4c5c695209 feat: musd-1428 update tooltip and how it works copy in money (#46617)
1bd3ee9d67 fix(settings): fix back navigation in full-screen settings (#46608)
1f48060f0e fix(confirmations): limit custom amount input to 2 decimals (#46569)
ba48071773 test: fix flaky test Contact Syncing - Existing User from inside MetaMask performs complete lifecycle: remote→local contact sync, add, modify, delete, verify sync on other device (#46614)
9b100c8a9d fix(metrics): persist MM Pay USD values for Transaction Finalized (#46611)
af3fc1b352 feat: musd-1423 allow deeplinking to money home (#46589)
64da086702 chore: update @metamask/notification-services-controller to version 29.0.1 [GE-516] (#46616)
ac13915c26 feat: musd-1424 add new home money balance widget (#46598)
ca1a71199c refactor(44551): extract wireStatePersistence seam and parsePortInfo/ loadPreinstalledSnaps helpers (#46565)
78e6f566e0 test: optimize delays in specs and methods (#46582)
12e4513291 test(confirmations): cover simpleSend hiding swap comparison UI (#45694)
ca97b63ed4 test: fix flaky test account tree sync after onboarding (#46578)
4b0ae42f29 feat(perps): investigate and improve deposit conversion for unfunded trade-screen users (#45986)
37dd8c747d feat(perps): preload Perps markets/metadata on unlock and instrument the loading path (match Mobile) (#46123)
65ba5fb8b4 fix: ledger checkbox (#46588)
40f3f5e4ee fix(settings): align backup and sync icons (#46599)
9bb04c0f30 refactor(44549): extract dapp and app-opened metrics from background.js (#46537)
439491750c fix(bft): wire linked-social profile detection for migration notice cp-13.50.0 (#46585)
6ab278599b fix(build): disable SWC mangling on the runtime chunk for AMO determinism (#46236)
e1a281f099 chore: upgrade design system packages to v73.0.0 cp-13.50.0 (#46593)
2b48773409 fix: update Swap tokens button on Activity empty state (#46587)
954c6d4d33 perf(persistence): cheapen split-state write telemetry measurement (#46423)
897924493d feat(accounts): redesign EVM-only private key export (#46331)
a51c91f3c5 feat: validate swap quotes with multi-asset fees (#46406)
fd4c4ca387 fix: hide sort control on NFT and empty DeFi tabs (#46559)
06a6bfec66 test: fix flaky Test Snap Metrics tests snap install metric (#46583)
0fc96a20a7 refactor(44550): extract toolbar badge manager from background.js (#46564)
1c2f8a5f7a fix(perps): add a "Top movers" section to the Perps tab (#46555)
fb2c0073aa fix: show prices for USD-native networks (#46129)
85fc51e2f7 feat: Migrate legacy userHandled passkey to PRF (#46560)
1d1b48dd03 fix: show failed toast for failed approved or signed transactions (#46556)
1179955dd7 feat: improve pop up flow for hyperliquid deposit prompt (#46222)
da710f7f08 chore(assets): remove dead legacy network filter modal (#46579)
a356fa58b7 fix(clipboardWrite): snap confirmation layout (#46533)
f5a93adb16 chore: Bump Snaps packages (#46580)
7b0839ee1f fix: flaky timeoutsMulti SRP..., Network Manager..., MetaMask onboarding... (#46571)
ad32a8c008 fix: rename notification detail clicked to match segment schema (#46574)
92b9742832 feat: add XDC Network to featured RPCs (#46543)
dbd3b2314a fix: added reserve balance for arc in swap and bridge flows cp-13.50.0 (#46517)
039ef2d93a fix(analytics): track EIP-712 properties for multichain signatures (#46467)
a5fc119201 fix(send): scope send max value mode to its own transaction (#46542)
d50a6314b8 perf(6402): clear react-hooks/refs in perps (#46019)
0a5a16b38f refactor(44548): extract active-tab tracker from background.js (#46536)
7219958e8c style(ui): increase TextField border radius (#46558)
f20773c960 fix(app-header): anchor unread notifications badge to the menu button (#46442)
71227d81d3 perf(6402): clear set-state-in-effect in platform UI and assets (#45423)
b69d0e344a refactor(ui): remove legacy checkbox and unused alert (#46525)
d2e016a5ad fix: carousel overlapping dapp control bar network menu (#46551)
7954f51e88 perf(6402): clear react-hooks/refs in shared hooks (#46016)
4bcec41a8c fix: enable money deposit and withdrawal when non-evm network selected in network filter (#46553)
9500ec4d75 fix: don’t show no fee label next to tokens without a no-fee route (#46552)
2c285766f3 feat: show appropriate titles for money account transactions from/to primary account (#46514)
c596f849c7 test: add conversion rates E2E for Injective, Chiliz, Plasma, Rootstock (#45498)
b4dc2c1c7d bump: align wallet transaction controller (#46548)
d513f29c9d fix: Increase Snaps idle timeout (#46550)
d705d36bde test: fix bug in the run-all-pw logic where a test failure stops all remaining pw tests (#46515)
8d27688f0d test: fix flaky test Transaction Finalized Event Successfully tracked when sending a transaction (#46544)
49cfd07bf5 feat: migrate marketing consent to AnalyticsController (#46473)
488766746b chore: unify vault recovery and critical error screens (#42740)
09ba726280 chore: Bump @metamask/delegation-deployments from ^1.3.0 to ^2.0.0 (#46194)
7a15470e3e test: fix flaky test Test Snap Homepage tests snap home page functionality (#46503)
fc86bf399a fix(confirmations): use pill-shaped reject-all action (#46479)
599f045896 test: Sync Feature Flag Registry - 2026-09-22 01:16 UTC (#46538)
c8dc9ef33b chore(navigation): remove swaps from navbar cp-13.50.0 (#46528)
d805ce3763 bump(design-system): upgrade MMDS React components (#46522)
07387b106e refactor(clipboardWrite): delete one more unused component (#46532)
5b4d4564e8 fix(assets): use pill-shaped token selector (#46521)
36749bcaea fix(ui): use pill-shaped product actions (#46481)
9a306257a6 fix(assets): use pill-shaped network filter action (#46478)
7130d9ce63 fix(perps): use pill-shaped close-all action (#46480)
d0506c79d9 fix: add remote flag to disable automatic post-update reload (#46436)
132066677e fix: duplicate toast on custom token import (#46516)
bed0056765 fix(earn): use pill-shaped account action cp-13.50.0 (#46475)
9936445be4 fix(money): skip generic toasts for known money-batch children (#46486)
2d0b3ba0de fix(onboarding): use pill-shaped settings action (#46476)
3eb555e703 perf(6402): clear react-hooks/refs in multichain and misc UI (#46021)
0f7d7f7849 fix: use asset chain's block explorer instead of current network's (#46508)
5b8637bb1d fix(ui): use pill-shaped core action buttons (#46474)
401998f1de fix(ramps): pluralize provider heading and style most reliable tag as info (#46507)
9630fd61ee fix(money): exclude hardware wallets from Money Account transactions (#46338)
4cc1e269a5 feat(swaps): add Sentry traces for bridge flows (#46428)
7a7b3a4b8c test: fix flaky test Send - Hex Address Normalization ERC20 normalizes typed address without 0x prefix and sends TST (#46499)
99e7a9c275 refactor(7405): convert provider-injection, updateTxData, and swaps-utils to TypeScript (#46430)
d99fab34ca test: fix flaky test Trezor add-wallet navigation (#46455)
99bddf57c7 test: fix flaky test Shield crypto payment modal covering Continue (#46418)
74a5df4656 fix(networks): report RouteMesh and rate-limited RPC URLs accurately (#46095)
0bde11fb0a test: Settings spec migration to Playwright (1) (#45396)
c787ea9fd8 refactor(ui): use HeaderSubpage for token details header (#46433)
9a0e1b33a1 chore: upgrade design system packages to v71.0.0 (#46470)
25c5619e02 fix(modal): update modal border radius (#46471)
ffcd81bdeb refactor(clipboardWrite): delete unused account components (#46429)
6adba1e645 Merge origin/main into stable-main-13.49.0
0f72c44ed9 release: Bump main version to 13.51.0 (#46439)
c3f562758f test: cover ETH to Robinhood and Total cost verification in the Select quote dialog (#45997)
b0f3bf244c fix(ramps): derive provider tags from the displayed quote (#46414)
2f02e2cff7 feat(bitcoin): bump snap-bitcoin 3.0.0 (#46432)
70f04d1efd test: migrate metrics traces and unlock specs to Playwright (#46422)
76cf6f6582 fix: revert changes from #46259 (#46416)
937e48d25b fix: clamp slippage tolerance to 100% maximum in bridge and swap settings (#46443)
a4f416d3a1 fix(activity): omit inflated amounts when EVM transfer decimals are missing (#46420)
1e874346d0 feat(core-backend): overide APIs with env var (#46272)

AI Test Plan

Risk Score High Risk Medium Risk Files Changed Commits
54/100 6 5 895 131
Cherry-Pick Scenarios (4)

High Risk Scenarios (1)

1. State Migrations - Crash resilience

Risk Level: HIGH

Why This Matters: Cherry-pick 46683 fixes a migration crash; regressions here can brick wallets or corrupt data on upgrade.

Test Steps:

  1. Use a profile from an older version with incomplete/edge-case state (e.g., missing preferences fields) and upgrade directly to 13.51.0.
  2. Unlock and verify the extension does not white-screen; wait for migrations to complete and for the home screen to load.
  3. Force close or reload the extension mid-migration, then reopen; confirm the migration resumes idempotently and completes.
  4. Validate accounts, networks, and assets are intact and no migration loops occur.

Medium Risk Scenarios (3)

1. Transaction Confirmations - Money account perps pay feature gating

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46709 gates a high-variance confirmation path; incorrect gating can expose unfinished UX or break confirmations.

Test Steps:

  1. With the feature flag DISABLED, attempt to initiate a money account 'perps pay' confirmation (via test dapp or deep link); verify the route/flow is hidden or blocked without errors.
  2. Attempt to access the disabled screen via a direct URL/route; confirm it's gated and does not render the pay UI.
  3. Enable the feature flag and retry; verify the perps pay confirmation renders and can be submitted successfully.

2. Analytics - Money account events parity

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46752 ensures parity with mobile tracking; event schema mistakes can harm analytics quality or cause runtime errors.

Test Steps:

  1. Enable MetaMetrics and from a money account perform Buy, Sell, Deposit, and Withdraw actions; verify analytics events are sent for each action (check DevTools Network).
  2. Repeat a single action twice; confirm exactly one event per action (no duplicates) with accurate properties (account type, chain, amount).
  3. Toggle MetaMetrics OFF and repeat an action; confirm no events are sent and the action still works normally.

3. Token Management - Stellar asset details via CAIP19 route

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46764 prioritizes route-derived CAIP19 IDs; incorrect precedence can show the wrong asset or crash the asset details page.

Test Steps:

  1. While on Stellar, navigate to an asset details page via a deep link containing a CAIP19 assetId in the route; verify the correct asset name, icon, and actions load.
  2. Open the same asset from the portfolio list and compare; details should match the route-driven view.
  3. Change only the route param to another Stellar asset and reload; confirm the view updates to the new asset and does not cling to stale state.
  4. If the route asset differs from state-selected asset, ensure the route-derived asset takes precedence without UI errors.

Release Scenarios (7)

High Risk Scenarios (5)

1. State Migrations (upgrade robustness)

Risk Level: HIGH

Why This Matters: Multiple migration files were modified and a new migration (228) was added; migration regressions can corrupt user data or block startup.

Test Steps:

  1. Install the previous stable extension (e.g., 13.50.x), create a wallet with 2 SRP accounts and 1 imported private key; add a custom network (e.g., Linea) and add custom tokens on at least two networks; connect to a dapp.
  2. Update to 13.51.0 and unlock; observe any migration banners/spinners and wait for the home view to render (no white screen/crash).
  3. Verify all accounts, connected sites, and custom networks are present; confirm tokens/NFTs appear once per network (no duplicates/missing items).
  4. Send a small token on two different networks and sign a message; confirm transactions and signatures complete successfully.
  5. Reload the extension and relaunch the browser; ensure no repeated migrations and state remains intact.

2. Onboarding - Passkey PRF Migration (successful path)

Risk Level: HIGH

Why This Matters: A new, user-facing migration flow was introduced; failures can block onboarding or leave keys/accounts unusable.

Test Steps:

  1. Use a profile eligible for the Passkey PRF migration (enable the feature/eligibility flag or seed state accordingly) and unlock.
  2. Confirm the Passkey PRF migration screen appears; select 'Migrate now' and complete the platform passkey prompt.
  3. After success, verify you return to the home screen; confirm account addresses remain the same and you can sign a message and send a small transaction.
  4. Lock and unlock again; verify the migration screen does not reappear.

3. Onboarding - Passkey PRF Migration (cancel, retry, and gating)

Risk Level: HIGH

Why This Matters: Edge cases (deferrals, rejections, ineligible users) must be resilient to avoid onboarding loops or broken states.

Test Steps:

  1. Trigger the migration screen and cancel (close the popup or choose 'Remind me later'); relaunch the extension.
  2. Verify the migration prompt returns and can resume without errors or duplicate prompts.
  3. For a non-eligible profile (no passkey), unlock and confirm the migration UI does not appear.
  4. Intentionally reject the platform passkey prompt; confirm a clear error is shown and the user can retry successfully.

4. Token/NFT Management - Asset lists across networks

Risk Level: HIGH

Why This Matters: Core asset selector logic changed; incorrect selectors/migrations can hide, duplicate, or misattribute assets across networks.

Test Steps:

  1. Before upgrading, add multiple watched tokens on Ethereum and another network (e.g., Linea) and add at least one NFT collection.
  2. Upgrade to 13.51.0 and review the Assets and NFTs tabs per network; ensure items persist, are not duplicated, and balances are correct.
  3. Toggle token detection off/on; verify manually-added tokens persist and are not auto-removed.
  4. Remove a watched token, then re-add it; confirm state updates save correctly and portfolio totals update as expected.

5. Session/Pending Approvals - App state (lock/unlock and resume)

Risk Level: HIGH

Why This Matters: App-state-controller changes can break how pending approvals survive lock/unlock and UI lifecycle transitions.

Test Steps:

  1. From a connected dapp, trigger a connect or signature request so that a confirmation appears in the extension.
  2. Lock the wallet while the confirmation is open, then unlock; verify the extension returns to the same pending approval.
  3. Close the extension UI and reopen; confirm the pending approval is still actionable and not lost.
  4. Initiate two concurrent requests (e.g., a connect plus a network switch); verify both can be processed without deadlocks or stale UI.

Medium Risk Scenarios (2)

1. Alerts & Notifications - Multiple alert modal and connection indicators

Risk Level: MEDIUM

Why This Matters: The unconnected-account alert component was removed and alert modal logic was changed; misbehavior can mislead users or block actions.

Test Steps:

  1. Connect a dapp with Account A, then switch to Account B that is not connected; verify the site connection indicator shows 'Not connected' and no legacy Unconnected Account banner appears.
  2. Create multiple alerts simultaneously (e.g., a pending signature and a failed network switch); verify the multiple-alert modal presents/queues them correctly and dismissing one reveals the next.
  3. Connect Account B via the connect button and confirm the connection status updates without ghost/overlapping banners.
  4. Navigate between tabs and reopen the extension; ensure dismissed alerts do not reappear and active alerts persist correctly.

2. Analytics/MetaMetrics - Consent gating and event integrity

Risk Level: MEDIUM

Why This Matters: Analytics controller and platform adapter changes risk over-collection, under-collection, or runtime errors that could impact core flows.

Test Steps:

  1. In Settings > Security & Privacy, toggle MetaMetrics OFF; perform a dapp connect and a token send; in DevTools Network, confirm no analytics calls are made.
  2. Toggle MetaMetrics ON; repeat the actions; confirm a single set of analytics calls fire per action (no duplicates).
  3. If an opt-in banner is shown, opt-out mid-session and repeat an action; confirm tracking stops and the UX itself remains unaffected.
  4. Reload the extension and verify the consent state persists.

Teams Sign-off Status

Signed off: None yet

Awaiting sign-off (7):
Accounts, Assets, Confirmations, Networks, Notifications, Onboarding, Transactions


Generated by AI Test Plan Analyzer (gpt-5) at 2026-10-01T02:16:18.543Z

AI generated test plan (JSON): test-plan-13.51.0.json

@HowardBraham

Copy link
Copy Markdown
Contributor

@metamaskbot update-attributions

@metamask-ci

metamask-ci Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Attributions updated

@metamask-ci

metamask-ci Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
Builds ready [be02bc1]
⚡ Performance Benchmarks (Total: 🟢 8 pass · 🟡 3 warn · 🔴 2 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 36806044714 | Baseline logs

Metricschrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 load_new_account(p95) [CI log]🔴 load_new_account(p95) [CI log]

Regressions (🔴 2 failures)

Interaction Benchmarks · Samples: 5 🔴 2
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 [CI log]
🔴 load_new_account
🔴 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ loadNewAccount/load_new_account: +418%
  • ↑ loadNewAccount/total: +418%
  • ↑ loadNewAccount/inp: +21%
  • ↑ loadNewAccount/fcp: +13%
  • ↑ confirmTx/longTaskCount: +33%
  • ↑ confirmTx/longTaskTotalDuration: +61%
  • ↑ confirmTx/longTaskMaxDuration: +74%
  • ↑ confirmTx/tbt: +76%
  • ↓ confirmTx/inp: -17%
  • ↑ confirmTx/fcp: +12%
  • ↑ confirmTx/lcp: +14%
  • ↑ bridgeUserActions/bridge_load_page: +69%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +155%
  • ↑ bridgeUserActions/longTaskCount: +67%
  • ↑ bridgeUserActions/longTaskTotalDuration: +54%
  • ↓ bridgeUserActions/longTaskMaxDuration: -12%
  • ↑ bridgeUserActions/tbt: +32%
  • ↑ bridgeUserActions/total: +16%
  • ↑ loadNewAccount/load_new_account: +418%
  • ↑ loadNewAccount/total: +418%
  • ↓ loadNewAccount/fcp: -61%
  • ↑ loadNewAccount/lcp: +1063%
  • ↓ confirmTx/longTaskCount: -100%
  • ↓ confirmTx/longTaskTotalDuration: -100%
  • ↓ confirmTx/longTaskMaxDuration: -100%
  • ↓ confirmTx/tbt: -100%
  • ↓ confirmTx/inp: -32%
  • ↓ confirmTx/fcp: -47%
  • ↑ confirmTx/lcp: +1078%
  • ↑ bridgeUserActions/bridge_load_page: +373%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +65%
  • ↓ bridgeUserActions/bridge_search_token: -12%
  • ↓ bridgeUserActions/longTaskCount: -100%
  • ↓ bridgeUserActions/longTaskTotalDuration: -100%
  • ↓ bridgeUserActions/longTaskMaxDuration: -100%
  • ↓ bridgeUserActions/tbt: -100%
  • ↑ bridgeUserActions/total: +30%
  • ↓ bridgeUserActions/fcp: -65%
  • ↑ bridgeUserActions/lcp: +1106%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 2.0s
  • 🟡 confirmTx/FCP: p75 1.9s
  • 🟡 bridgeUserActions/FCP: p75 1.9s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ startupStandardHome/firstPaint: +14%
  • ↓ startupStandardHome/firstReactRender: -99%
  • ↓ startupStandardHome/initialActions: -33%
  • ↑ startupStandardHome/setupStore: +36%
  • ↑ startupStandardHome/numNetworkReqs: +10%
  • ↓ startupStandardHome/longTaskCount: -33%
  • ↑ startupStandardHome/lcp: +16%
  • ↓ startupStandardHome/domInteractive: -14%
  • ↓ startupStandardHome/firstReactRender: -98%
  • ↓ startupStandardHome/initialActions: -50%
  • ↑ startupStandardHome/setupStore: +163%
User Journey Benchmarks · Samples: 5 · real API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 total
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ onboardingImportWallet/confirmSrpToPwForm: +12%
  • ↓ onboardingImportWallet/doneButtonToHomeScreen: -94%
  • ↓ onboardingImportWallet/openAccountMenuToAccountListLoaded: -75%
  • ↓ onboardingImportWallet/longTaskCount: -86%
  • ↓ onboardingImportWallet/longTaskTotalDuration: -90%
  • ↓ onboardingImportWallet/longTaskMaxDuration: -81%
  • ↓ onboardingImportWallet/tbt: -92%
  • ↓ onboardingImportWallet/total: -89%
  • ↑ onboardingNewWallet/srpButtonToPwForm: +33%
  • ↑ onboardingNewWallet/createPwToRecoveryScreen: +20%
  • ↑ onboardingNewWallet/skipBackupToMetricsScreen: +21%
  • ↑ onboardingNewWallet/agreeButtonToOnboardingSuccess: +34%
  • ↓ onboardingNewWallet/doneButtonToAssetList: -73%
  • ↓ onboardingNewWallet/longTaskCount: -100%
  • ↓ onboardingNewWallet/longTaskTotalDuration: -100%
  • ↓ onboardingNewWallet/longTaskMaxDuration: -100%
  • ↓ onboardingNewWallet/tbt: -100%
  • ↓ onboardingNewWallet/total: -70%
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↓ dappPageLoad/pageLoadTime: -67%
  • ↓ dappPageLoad/domContentLoaded: -23%
  • ↓ dappPageLoad/firstPaint: -55%
  • ↓ dappPageLoad/firstContentfulPaint: -55%
Bundle Size Diffs [🚨 Warning! Bundle size has increased!]
Status Bundle Total Diff Change
✅ background 15.49 MiB +631 Bytes 0.00%
🚨 ui 19.01 MiB +11.78 KiB +0.06%
✅ common 0 Bytes 0 Bytes 0.00%
✅ other 1.98 MiB 0 Bytes 0.00%
✅ content scripts 1.97 MiB +12 Bytes 0.00%
✅ zip 23.57 MiB -35.9 KiB -0.15%
### 🍒 What's in this RC

Cherry-picks (11 commits)
Commit Description
be02bc1e30 Update Attributions
53752b4eb8 release(runway): cherry-pick chore: audit webpack-dev-middleware and undici for security advisories (#46827)
c3457acf2f release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46824)
08d1af6a46 release: release-changelog/13.51.0 (#46635)
d0ef1938e0 Update Attributions
cc0f89183b release(runway): cherry-pick fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components (#46764)
03039f7afa release(runway): cherry-pick fix(money): remove Paid by MetaMask from transaction fee tooltips (#46746)
601998bb20 release(runway): cherry-pick feat: ensure parity with mobile money account events tracking (#46752)
b138b9aa2f release(runway): cherry-pick fix(confirmations): gate money account perps pay on its feature flag (#46709)
11ef94709f Merge pull request #46693 from MetaMask/stable-sync-release-13.51.0
fcb4edcf43 Merge branch 'release/13.51.0' into stable-sync-release-13.51.0

Changelog (128 commits since v13.50.0)
Commit Description
be02bc1e30 Update Attributions
53752b4eb8 release(runway): cherry-pick chore: audit webpack-dev-middleware and undici for security advisories (#46827)
c3457acf2f release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46824)
08d1af6a46 release: release-changelog/13.51.0 (#46635)
d0ef1938e0 Update Attributions
cc0f89183b release(runway): cherry-pick fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components (#46764)
03039f7afa release(runway): cherry-pick fix(money): remove Paid by MetaMask from transaction fee tooltips (#46746)
601998bb20 release(runway): cherry-pick feat: ensure parity with mobile money account events tracking (#46752)
b138b9aa2f release(runway): cherry-pick fix(confirmations): gate money account perps pay on its feature flag (#46709)
e048b5070e release(runway): cherry-pick fix: Fix migration crash (#46683)
fbb2ecd9f0 release(runway): cherry-pick fix: update Basic Functionality privacy messaging (#46640)
c8e80f78e7 Merge release/13.50.0 into release/13.51.0
eac5d1074d refactor(bundle-size): polish pr comment layout (#41912)
109504d7c4 fix(ramps): explain quote unavailability in pay-with warning tooltip (#46505)
1729f4bb5c feat(clipboardWrite): remove the clipboardWrite permission from the manifest file (#46603)
3e6f28dc69 fix(client-mcp-core): update to 0.10.0 (#46596)
8f5a593df3 fix(ramps): validate quote buy URL before fetching the buy widget (#46504)
117a8f3bc1 feat: tag scam questionnaire support visits (#46590)
f43cc73e73 test: ignore console error in Tron account derivation (#46624)
f0e645dc4b fix: handle resolved scam questionnaire domains (#46568)
b460c61144 fix: MUSD-1385 use the up-right arrow icon for Sent money activity rows (#46457)
6b0c405ac2 chore: improve spacing between elements on money home (#46619)
2eaf1af858 feat(nfts): show basic functionality off empty state in NftsTab and update the links cp-13.50.0 (#46557)
b11264e482 chore: New Crowdin Translations by GitHub Action cp-13.50.0 (#46512)
16e8dadf57 chore(activity): remove client side from/to filter (#46605)
b7a1e813c4 refactor(settings): proper settings index route (#46609)
81a9d511dc refactor(7371): convert migrations 027–053 to TypeScript (#46604)
4c5c695209 feat: musd-1428 update tooltip and how it works copy in money (#46617)
1bd3ee9d67 fix(settings): fix back navigation in full-screen settings (#46608)
1f48060f0e fix(confirmations): limit custom amount input to 2 decimals (#46569)
ba48071773 test: fix flaky test Contact Syncing - Existing User from inside MetaMask performs complete lifecycle: remote→local contact sync, add, modify, delete, verify sync on other device (#46614)
9b100c8a9d fix(metrics): persist MM Pay USD values for Transaction Finalized (#46611)
af3fc1b352 feat: musd-1423 allow deeplinking to money home (#46589)
64da086702 chore: update @metamask/notification-services-controller to version 29.0.1 [GE-516] (#46616)
ac13915c26 feat: musd-1424 add new home money balance widget (#46598)
ca1a71199c refactor(44551): extract wireStatePersistence seam and parsePortInfo/ loadPreinstalledSnaps helpers (#46565)
78e6f566e0 test: optimize delays in specs and methods (#46582)
12e4513291 test(confirmations): cover simpleSend hiding swap comparison UI (#45694)
ca97b63ed4 test: fix flaky test account tree sync after onboarding (#46578)
4b0ae42f29 feat(perps): investigate and improve deposit conversion for unfunded trade-screen users (#45986)
37dd8c747d feat(perps): preload Perps markets/metadata on unlock and instrument the loading path (match Mobile) (#46123)
65ba5fb8b4 fix: ledger checkbox (#46588)
40f3f5e4ee fix(settings): align backup and sync icons (#46599)
9bb04c0f30 refactor(44549): extract dapp and app-opened metrics from background.js (#46537)
439491750c fix(bft): wire linked-social profile detection for migration notice cp-13.50.0 (#46585)
6ab278599b fix(build): disable SWC mangling on the runtime chunk for AMO determinism (#46236)
e1a281f099 chore: upgrade design system packages to v73.0.0 cp-13.50.0 (#46593)
2b48773409 fix: update Swap tokens button on Activity empty state (#46587)
954c6d4d33 perf(persistence): cheapen split-state write telemetry measurement (#46423)
897924493d feat(accounts): redesign EVM-only private key export (#46331)
a51c91f3c5 feat: validate swap quotes with multi-asset fees (#46406)
fd4c4ca387 fix: hide sort control on NFT and empty DeFi tabs (#46559)
06a6bfec66 test: fix flaky Test Snap Metrics tests snap install metric (#46583)
0fc96a20a7 refactor(44550): extract toolbar badge manager from background.js (#46564)
1c2f8a5f7a fix(perps): add a "Top movers" section to the Perps tab (#46555)
fb2c0073aa fix: show prices for USD-native networks (#46129)
85fc51e2f7 feat: Migrate legacy userHandled passkey to PRF (#46560)
1d1b48dd03 fix: show failed toast for failed approved or signed transactions (#46556)
1179955dd7 feat: improve pop up flow for hyperliquid deposit prompt (#46222)
da710f7f08 chore(assets): remove dead legacy network filter modal (#46579)
a356fa58b7 fix(clipboardWrite): snap confirmation layout (#46533)
f5a93adb16 chore: Bump Snaps packages (#46580)
7b0839ee1f fix: flaky timeoutsMulti SRP..., Network Manager..., MetaMask onboarding... (#46571)
ad32a8c008 fix: rename notification detail clicked to match segment schema (#46574)
92b9742832 feat: add XDC Network to featured RPCs (#46543)
dbd3b2314a fix: added reserve balance for arc in swap and bridge flows cp-13.50.0 (#46517)
039ef2d93a fix(analytics): track EIP-712 properties for multichain signatures (#46467)
a5fc119201 fix(send): scope send max value mode to its own transaction (#46542)
d50a6314b8 perf(6402): clear react-hooks/refs in perps (#46019)
0a5a16b38f refactor(44548): extract active-tab tracker from background.js (#46536)
7219958e8c style(ui): increase TextField border radius (#46558)
f20773c960 fix(app-header): anchor unread notifications badge to the menu button (#46442)
71227d81d3 perf(6402): clear set-state-in-effect in platform UI and assets (#45423)
b69d0e344a refactor(ui): remove legacy checkbox and unused alert (#46525)
d2e016a5ad fix: carousel overlapping dapp control bar network menu (#46551)
7954f51e88 perf(6402): clear react-hooks/refs in shared hooks (#46016)
4bcec41a8c fix: enable money deposit and withdrawal when non-evm network selected in network filter (#46553)
9500ec4d75 fix: don’t show no fee label next to tokens without a no-fee route (#46552)
2c285766f3 feat: show appropriate titles for money account transactions from/to primary account (#46514)
c596f849c7 test: add conversion rates E2E for Injective, Chiliz, Plasma, Rootstock (#45498)
b4dc2c1c7d bump: align wallet transaction controller (#46548)
d513f29c9d fix: Increase Snaps idle timeout (#46550)
d705d36bde test: fix bug in the run-all-pw logic where a test failure stops all remaining pw tests (#46515)
8d27688f0d test: fix flaky test Transaction Finalized Event Successfully tracked when sending a transaction (#46544)
49cfd07bf5 feat: migrate marketing consent to AnalyticsController (#46473)
488766746b chore: unify vault recovery and critical error screens (#42740)
09ba726280 chore: Bump @metamask/delegation-deployments from ^1.3.0 to ^2.0.0 (#46194)
7a15470e3e test: fix flaky test Test Snap Homepage tests snap home page functionality (#46503)
fc86bf399a fix(confirmations): use pill-shaped reject-all action (#46479)
599f045896 test: Sync Feature Flag Registry - 2026-09-22 01:16 UTC (#46538)
c8dc9ef33b chore(navigation): remove swaps from navbar cp-13.50.0 (#46528)
d805ce3763 bump(design-system): upgrade MMDS React components (#46522)
07387b106e refactor(clipboardWrite): delete one more unused component (#46532)
5b4d4564e8 fix(assets): use pill-shaped token selector (#46521)
36749bcaea fix(ui): use pill-shaped product actions (#46481)
9a306257a6 fix(assets): use pill-shaped network filter action (#46478)
7130d9ce63 fix(perps): use pill-shaped close-all action (#46480)
d0506c79d9 fix: add remote flag to disable automatic post-update reload (#46436)
132066677e fix: duplicate toast on custom token import (#46516)
bed0056765 fix(earn): use pill-shaped account action cp-13.50.0 (#46475)
9936445be4 fix(money): skip generic toasts for known money-batch children (#46486)
2d0b3ba0de fix(onboarding): use pill-shaped settings action (#46476)
3eb555e703 perf(6402): clear react-hooks/refs in multichain and misc UI (#46021)
0f7d7f7849 fix: use asset chain's block explorer instead of current network's (#46508)
5b8637bb1d fix(ui): use pill-shaped core action buttons (#46474)
401998f1de fix(ramps): pluralize provider heading and style most reliable tag as info (#46507)
9630fd61ee fix(money): exclude hardware wallets from Money Account transactions (#46338)
4cc1e269a5 feat(swaps): add Sentry traces for bridge flows (#46428)
7a7b3a4b8c test: fix flaky test Send - Hex Address Normalization ERC20 normalizes typed address without 0x prefix and sends TST (#46499)
99e7a9c275 refactor(7405): convert provider-injection, updateTxData, and swaps-utils to TypeScript (#46430)
d99fab34ca test: fix flaky test Trezor add-wallet navigation (#46455)
99bddf57c7 test: fix flaky test Shield crypto payment modal covering Continue (#46418)
74a5df4656 fix(networks): report RouteMesh and rate-limited RPC URLs accurately (#46095)
0bde11fb0a test: Settings spec migration to Playwright (1) (#45396)
c787ea9fd8 refactor(ui): use HeaderSubpage for token details header (#46433)
9a0e1b33a1 chore: upgrade design system packages to v71.0.0 (#46470)
25c5619e02 fix(modal): update modal border radius (#46471)
ffcd81bdeb refactor(clipboardWrite): delete unused account components (#46429)
6adba1e645 Merge origin/main into stable-main-13.49.0
0f72c44ed9 release: Bump main version to 13.51.0 (#46439)
c3f562758f test: cover ETH to Robinhood and Total cost verification in the Select quote dialog (#45997)
b0f3bf244c fix(ramps): derive provider tags from the displayed quote (#46414)
2f02e2cff7 feat(bitcoin): bump snap-bitcoin 3.0.0 (#46432)
70f04d1efd test: migrate metrics traces and unlock specs to Playwright (#46422)
76cf6f6582 fix: revert changes from #46259 (#46416)
937e48d25b fix: clamp slippage tolerance to 100% maximum in bridge and swap settings (#46443)
a4f416d3a1 fix(activity): omit inflated amounts when EVM transfer decimals are missing (#46420)
1e874346d0 feat(core-backend): overide APIs with env var (#46272)

AI Test Plan

Risk Score High Risk Medium Risk Files Changed Commits
59/100 7 7 895 132
Cherry-Pick Scenarios (5)

High Risk Scenarios (2)

1. Migration Stability (crash on upgrade)

Risk Level: HIGH

Why This Matters: Cherry-pick 46683 fixes a migration crash that could block users on upgrade; verifying recovery and data integrity is critical.

Test Steps:

  1. Use a state file from a version known to trigger migration 28–51 paths, then update directly to 13.51.0.
  2. Open the extension and observe for crashes/blank screen; if it occurs, reload the extension.
  3. Confirm the extension recovers without a loop and that accounts/assets/permissions are intact.
  4. Lock/unlock and navigate major views to ensure stability post-recovery.

2. Confirmations: Money Account Perps Pay feature flag gating

Risk Level: HIGH

Why This Matters: Cherry-pick 46709 gates a transaction-type UI by feature flag. Incorrect gating can expose unfinished flows or block valid transactions.

Test Steps:

  1. With the feature flag OFF (Settings > Experimental), trigger a Money Account Perps Pay flow (from a supported dapp or staging environment).
  2. Verify the special Perps Pay UI does not appear and the transaction cannot proceed via that path.
  3. Enable the flag and repeat; confirm the Perps Pay-specific UI now appears and can complete.
  4. Initiate a standard send/approval to ensure non-Perps flows remain unaffected.

Medium Risk Scenarios (3)

1. Stellar Asset Details: CAIP-19 route-derived ID priority

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46764 prioritizes route-derived CAIP-19 IDs. Without this, users could see the wrong Stellar asset details.

Test Steps:

  1. Navigate to a Stellar asset details page via a route/deeplink containing a CAIP-19 asset ID.
  2. Verify the asset displayed matches the route-provided CAIP-19 (symbol, issuer, balance) even if local state has a different/default entry.
  3. Navigate to the same asset from the in-app list and confirm details remain consistent.
  4. Refresh the page (or reopen the extension) and confirm persistence.

2. Privacy Messaging: Basic Functionality copy and settings

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46640 fixes Basic Functionality privacy messaging; incorrect copy or rendering can mislead users about data collection.

Test Steps:

  1. Fresh install; proceed to the privacy/metrics consent screen.
  2. Verify the updated Basic Functionality messaging appears exactly once and matches expected copy.
  3. Toggle consent and proceed; confirm no blocking or layout issues and settings reflect the chosen option.
  4. Visit Settings > Security & Privacy to verify the choice is stored.

3. Analytics parity for Money Account events

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46752 ensures parity with mobile for Money Account analytics. Misfired or missing events break cross-platform analytics consistency.

Test Steps:

  1. Opt in to MetaMetrics and enable Money Account (if applicable in your environment).
  2. Perform a representative Money Account action (e.g., open the Money Account view, start an add-funds flow).
  3. Verify expected analytics events are emitted (names/properties as per parity) via network/logs.
  4. Opt out and repeat to confirm no events are sent when opted out.

Release Scenarios (9)

High Risk Scenarios (5)

1. State Migrations (upgrade from prior versions)

Risk Level: HIGH

Why This Matters: Many migration files changed and a new migration 228 was added. Any migration fault can brick wallets on upgrade or silently drop assets/connections.

Test Steps:

  1. Start with a profile on a previous stable (e.g., 13.50.x) containing: 2+ accounts, imported tokens/NFTs across 2 networks, and 1-2 connected sites.
  2. Update the same profile to 13.51.0 and open the extension until the home screen loads.
  3. Verify accounts, balances, detected/imported tokens and NFTs persist; confirm connected sites remain in Settings > Connected Sites.
  4. Lock and unlock; switch networks; confirm no infinite spinner, no blank screens, and that activity history remains intact.

2. Onboarding - Passkey PRF Migration (success path)

Risk Level: HIGH

Why This Matters: New passkey PRF migration page/hook was added. Any break can block unlock, strand users on a modal, or degrade passkey login.

Test Steps:

  1. Use a profile from a prior version that had passkey enabled (or configured) and update to 13.51.0.
  2. Unlock; when the Passkey PRF Migration screen appears, choose Migrate Now and complete prompts.
  3. Confirm success returns you to the home screen with no further prompts.
  4. Lock and re-unlock using passkey to ensure sign-in still works post-migration.

3. Alert System Regression (removed Unconnected Account Alert + multiple-alert-modal updates)

Risk Level: HIGH

Why This Matters: The unconnected account alert UI was removed and multiple-alert-modal changed. This can cause missing prompts, stuck modals, or mis-queued critical requests.

Test Steps:

  1. Connect dapp Site A to Account 1 only; switch the extension to Account 2 (not connected).
  2. From Site A, initiate a request (e.g., eth_sendTransaction or signature).
  3. Verify you get an appropriate connection/permissions flow (no missing or stuck UI), and can proceed after connecting or switching accounts.
  4. Trigger two simultaneous alerts (e.g., Site A requests connection while also requesting add network) and ensure the multiple-alert modal queues and navigates between alerts correctly.

4. Assets: Token/NFT discovery and selectors (cross-network)

Risk Level: HIGH

Why This Matters: Selectors for assets were heavily updated. Any mistake can misreport balances, hide tokens/NFTs, or double-count across networks.

Test Steps:

  1. On Mainnet and one L2 (e.g., Linea), import 2 ERC-20 tokens (one auto-detected, one manual) and 1 NFT.
  2. Restart the extension; confirm Assets and NFTs tabs show correct symbols, balances, and totals on both networks.
  3. Switch networks rapidly (Mainnet <-> L2) and confirm lists update correctly without duplicates or disappearing items.
  4. Hide/unhide a token and ensure it persists correctly across sessions and networks.

5. Analytics & Privacy Controls (controllers and platform adapter)

Risk Level: HIGH

Why This Matters: Analytics controller and adapter changed. Incorrect gating could leak data when opted out or drop critical events when opted in.

Test Steps:

  1. Fresh install; opt out of MetaMetrics during onboarding.
  2. Connect to a test dapp and perform 2 key actions (connect, simple send). Observe that no analytics calls are made (via network panel or logs).
  3. Opt in to MetaMetrics in Settings > Security & Privacy and repeat actions.
  4. Verify analytics requests are sent only when opted in and that toggling immediately changes behavior without reload.

Medium Risk Scenarios (4)

1. Passkey PRF Migration (skip/defer and re-prompt behavior)

Risk Level: MEDIUM

Why This Matters: Defer flows can loop or suppress future prompts. Users must never be blocked from core actions or lose the ability to migrate later.

Test Steps:

  1. On a profile where the Passkey PRF Migration screen appears, choose to skip or remind me later.
  2. Navigate around the app (Assets, Activity) and lock/unlock.
  3. Confirm you’re not blocked by the migration prompt, and verify expected re-prompt timing/conditions (e.g., shown once per session or after a defined trigger).
  4. Later, choose to migrate and validate the success path still completes as expected.

2. Transaction Confirmation Flow (general regression, EIP-1559 UI)

Risk Level: MEDIUM

Why This Matters: Broad UI changes raise the risk of confirmation regressions that can mislead users about fees or hinder signing.

Test Steps:

  1. From a dapp, initiate an ETH send; open the confirmation and inspect gas/fee controls and tooltips.
  2. Switch gas presets (low/market/high) and edit advanced gas; confirm fees recalculate and UI remains responsive.
  3. Confirm the transaction; verify it submits and status updates in Activity correctly.
  4. Repeat on a different network (e.g., L2) to ensure parity and no tooltip/layout overlap.

3. Asset List Filter: Home network vs All networks

Risk Level: MEDIUM

Why This Matters: Filter logic impacts asset visibility and totals. A broken filter can confuse users about balances and holdings.

Test Steps:

  1. Open the Assets list; open the Home Network Filter modal.
  2. Toggle between Home network and All networks; verify counts and asset entries update immediately.
  3. Switch accounts and return; ensure filter choice persists and lists remain accurate.
  4. Restart the extension and confirm the last-used filter is remembered.

4. Connected Sites and Permissions after migration

Risk Level: MEDIUM

Why This Matters: Migrations and alert changes can inadvertently drop or corrupt site permissions, breaking dapp connectivity.

Test Steps:

  1. Before upgrade, connect 2 dapps (different chains) and grant permissions (view accounts, suggest chain).
  2. Upgrade to 13.51.0 and open Settings > Connected Sites.
  3. Verify sites and permissions persist; from each dapp, call eth_accounts and ensure the expected account is returned.
  4. Revoke one site and ensure it’s removed and future calls re-trigger a permission request.

Teams Sign-off Status

Signed off: None yet

Awaiting sign-off (9):
Accounts, Assets, Confirmations, Networks, Onboarding, Permissions, Security, Settings, Transactions


Generated by AI Test Plan Analyzer (gpt-5) at 2026-10-01T02:52:21.573Z

AI generated test plan (JSON): test-plan-13.51.0.json

@sleepytanya

Copy link
Copy Markdown
Contributor

@SocketSecurity ignore npm/@metamask/client-mcp-core@0.10.0
@SocketSecurity ignore npm/@metamask/device-mcp@0.4.2
@SocketSecurity ignore npm/@modelcontextprotocol/sdk@1.30.0
@SocketSecurity ignore npm/json-schema-typed@8.0.2

@sleepytanya
sleepytanya marked this pull request as ready for review October 9, 2026 04:50

@cursor cursor Bot 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.

Modern Web Guidance review

Reviewed UI changes in this release against modern-web-guidance (accessibility + interest-triggered tooltips). Actionable findings are inline on the ramps payment-method quote tooltip work.

Summary

  • Nested interactive controls on payment-method rows (selectable row + warning tooltip trigger).
  • Missing accessible name on the icon-only warning trigger.
  • Native popover="hint" positioning relies on position-area without explicit anchor pairing required when implicit anchors are unavailable.

No other clear modern-web-guidance regressions stood out in the UI diff scan (240 TSX files); remaining patterns largely follow existing extension conventions.

Open in Web View Automation 

Sent by Cursor Automation: Modern web guidance

alignItems={BoxAlignItems.Center}
justifyContent={BoxJustifyContent.Between}
gap={3}
<div // NOSONAR: hosts a nested tooltip trigger that a native <button> cannot contain; keyboard support is implemented below

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.

Nested interactive controls (accessibility): This row is exposed as a single control (role="button" via ButtonBase asChild), but RampsQuoteDisplay renders a nested <button> for the warning tooltip trigger when showWarningIcon is set.

Interactive elements must not contain other interactive elements. Screen readers and keyboard users get ambiguous focus targets, and HTML validity checks fail.

Suggested direction: Keep the tooltip trigger outside the row activation control (e.g. sibling layout), or make the row a non-interactive container with two separate focusable controls (primary row action + warning trigger), rather than wrapping both in one button-like surface.

onMouseLeave: handlePointerLeave,
})}
>
<button

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.

Accessible name (accessibility): This trigger is icon-only and has no aria-label or visible text. Every interactive control needs an accessible name; reuse the tooltip copy or a short i18n string (e.g. “Quote unavailable, show details”).

onClick={(event) => event.stopPropagation()}
onKeyDown={(event) => event.stopPropagation()}
data-testid="ramps-quote-display-warning-tooltip"
className="m-0 max-w-[250px] rounded-lg border border-border-muted bg-background-default p-4 text-text-default shadow-md [position-area:bottom]"

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.

Native tooltip positioning (interest-triggered tooltips): Positioning uses only [position-area:bottom]. For popover="hint" + interestfor, use explicit anchor pairing when implicit anchors are unsupported: unique anchor-name on the trigger, position-anchor on the popover, anchor() insets, and a position-try fallback (e.g. flip-block). Without that, the native path can misplace the hint in browsers that support Interest Invokers but not implicit anchor positioning.

This branch was previously deployed

2 inactive deployments
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.51.0 Issue or pull request that will be included in release 13.51.0 team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.