Skip to content

release: 13.52.0 - #46848

Merged
DDDDDanica merged 118 commits into
stablefrom
release/13.52.0
Oct 14, 2026
Merged

DDDDDanica merged 118 commits into
stablefrom
release/13.52.0

Conversation

@metamaskbot

@metamaskbot metamaskbot commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

🚀 v13.52.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
  • Design System
  • Earn
  • Emerging Opportunities
  • Extension Platform
  • Money Movement
  • Networks
  • Onboarding
  • Perps
  • Subscriptions

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

0xmariniere and others added 30 commits September 24, 2026 18:46
## **Description**

Updates the OP Mainnet network icon (`app/images/optimism.svg`) to the
current official Optimism brand asset.

1. **Reason for the change:** the asset currently shipped in the
extension is an outdated version of the OP mark, authored at 360x360
with the logo nested inside a translated/scaled `<g>` wrapper.
2. **Improvement/solution:** replaces it with the current OP mark drawn
as a single path on a 1024x1024 viewBox, so it renders crisply at every
size the wallet uses for network icons and matches the mark used across
Optimism surfaces.

Asset-only change, no code paths touched. A matching PR against
`metamask-mobile` will follow so both clients ship the same asset.

## **Changelog**

CHANGELOG entry: Updated the OP Mainnet network icon to the current
Optimism brand asset

## **Related issues**

N/A

## **Manual testing steps**

1. Build and run the extension.
2. Open the network picker and confirm the OP Mainnet icon renders
correctly.
3. Switch to OP Mainnet and confirm the icon renders correctly in the
network pill, the account overview and the asset list rows.
4. Confirm the mark is sharp and correctly framed at small sizes, with
no clipping or stretching.

## **Screenshots/Recordings**

### **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]
> **Low Risk**
> Static asset swap with no runtime or security impact; existing
references keep the same path.
> 
> **Overview**
> Replaces **`app/images/optimism.svg`** with the current Optimism brand
mark. The old 360×360 asset used a scaled group wrapper; the new file is
a **1024×1024** canvas with a single path on a red background
(`#FF0421`) and off-white mark (`#FAFAF9`), so OP Mainnet icons stay
sharp in the network picker, network pill, and related UI that already
point at this file (e.g. `OPTIMISM_TOKEN_IMAGE_URL`).
> 
> **No application logic changes**—only the bundled SVG.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cafdbf2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#46600)

## **Description**

Moves ownership of the marketing campaign cookie id
(`marketingCampaignCookieId`) from `MetaMetricsController` to
`AnalyticsController`, which now supports the field as of
`@metamask/analytics-controller@3.2.0` (core PR #10373).

**Context:** `AnalyticsController` already owns the marketing consent
state (`optedInToMarketing`, `marketingConsentDecisionMade`), and core
#10373 added the cookie id plus a
`AnalyticsController:setMarketingCampaignCookieId` messenger action to
it, with controller-side auto-clear on `optOutOfMarketing` and
`resetMarketingConsentDecision`. This completes the migration on the
extension side and resolves [mcmire's comment on
#46473](#46473 (comment)):
the clearing is now a controller action invoked through the messenger,
and the manual clearing blocks are gone from `analytics.ts`.

**Solution:**
- Dependency bumped to stable `@metamask/analytics-controller@^3.2.0`
(no preview builds).
- Migration `229` moves the persisted cookie id value from
`MetaMetricsController` state to `AnalyticsController` state, preserving
both the string and the historically-supported numeric forms.
- `MetaMetricsController` no longer holds any state or exposed methods
(the controller shell remains).
- The platform adapter reads the cookie via
`AnalyticsController:getState`; `metamask-controller.js` writes via the
new controller action.
- `setParticipateInMetaMetrics` no longer clears the cookie at all, and
`setDataCollectionForMarketing` no longer clears it manually — the
controller handles clearing. **Accepted behavior change:** the cookie id
is retained after a full metrics opt-out / onboarding reset while
marketing consent remains on (core's `optOut`/`resetConsentDecision`
intentionally do not clear it).
- Flat-state type, Sentry-state flag, E2E fixtures/snapshots and unit
tests follow the new owner.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

Depends on: MetaMask/core#10373 (shipped in
`@metamask/analytics-controller@3.2.0`)
Follow-up context:
#46473 (comment)

## **Manual testing steps**

1. Enable the marketing consent toggle during onboarding (or via
Settings → Privacy → Data collection for marketing toggle).
2. Visit a website (e.g. metamask.io), open the inspector console, and
execute the following:

```
window.postMessage(
  {
    target: 'metamask-contentscript',
    data: {
      name: 'metamask-cookie-handler',
      data: {
        jsonrpc: '2.0',
        method: 'getCookieFromMarketingPage',
        params: [{ ga_client_id: 99999, origin: window.location.origin }],
        id: Date.now(),
        origin: window.location.origin,
      },
    },
  },
  window.location.origin,
);
```
3. Confirm AnalyticsController state has marketingCampaignCookieId set
to 99999 (check in the inspector).
4. Toggle "Data collection for marketing" off in Privacy Settings and
confirm marketingCampaignCookieId is cleared.
5. Do the same steps again with marketing consent toggle disabled during
onboarding and confirm marketingCampaignCookieId is not set.

<!--
## **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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches persisted analytics/marketing identity state and consent
clearing behavior across upgrades; migration 229 must run correctly for
existing users.
> 
> **Overview**
> **`marketingCampaignCookieId`** is moved from
**`MetaMetricsController`** to **`AnalyticsController`** (via
**`@metamask/analytics-controller@^3.2.0`**), with migration **229**
copying persisted string/numeric values and stripping the field from
**`MetaMetricsController`**, which is now an empty controller shell.
> 
> Reads and writes go through **`AnalyticsController:getState`** and
**`AnalyticsController:setMarketingCampaignCookieId`** (platform
adapter, marketing-page cookie handler, messenger delegation). Extension
helpers **`setParticipateInMetaMetrics`** /
**`setDataCollectionForMarketing`** no longer manually null the cookie;
clearing is delegated to core on marketing opt-out. **Opting out of
MetaMetrics alone no longer clears the cookie** (intentional behavior
change). Types, Sentry flags, fixtures, and tests follow the new owner.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4ac3158. 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**

Opening the wallet crashed with `new BigNumber() not a number:
5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` (Solana mainnet CAIP reference). The
crash was in the unified-assets migration selectors in
`shared/lib/selectors/assets-migration.ts`, which remap `assetsBalance`
onto the legacy EVM controllers.

Those selectors filtered EVM accounts, then treated every asset under
them as EVM and passed the CAIP chain reference to
`decimalToPrefixedHex`. A Solana native asset stored under an EVM
account made that call throw and the UI error boundary took over.

This PR skips any asset whose CAIP namespace is not `eip155` before the
hex conversion, matching the other selectors in the same file.

## **Changelog**

CHANGELOG entry: Fixed a crash that occurred when unified asset state
included a non-EVM asset on an EVM account

## **Related issues**

Fixes: #46636

## **Manual testing steps**

1. Build and load the extension with unified assets enabled
(`ASSETS_UNIFIED_STATE_ENABLED` and the remote flag).
2. Unlock a wallet that has both an EVM account and Solana assets (or
state where a Solana native asset is present under an EVM account in
`assetsBalance`).
3. Open the home screen.
4. Confirm the wallet home renders and does not show "Your information
can't be shown".
5. Confirm ETH / EVM balances still display.

<!--
## **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
- [ ] 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**
> Touches home-screen balance/token selectors on the unified-assets
path; the change is narrow defensive filtering with new regression
tests, but incorrect filtering could hide or mis-map balances.
> 
> **Overview**
> Fixes a wallet crash when unified asset state includes **non-EVM
assets (e.g. Solana) on an EVM account** in `assetsBalance`. The
unified-assets migration selectors in `assets-migration.ts` already
limited work to EVM accounts but still ran every asset through EVM-only
steps like `decimalToPrefixedHex`, which throws on Solana CAIP chain
references.
> 
> **Account tracker, tokens, and token-balances** migration selectors
now **skip assets whose CAIP namespace is not `eip155`** before hex
conversion and legacy controller shaping, consistent with other filters
in the same module.
> 
> Unit tests cover Solana native/token balances attached to an
`eip155:eoa` account and assert only EVM data is emitted without
throwing.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
70f5477. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Updated the Basic Functionality migration messaging to match the latest
copy from the product/design discussion.

- Reworded the SRP migration toast to explain the consolidated Basic
Functionality setting and preserve the on/off state.
- Reworded the social-login modal to explain profile and wallet-address
storage, keep Basic Functionality enabled for social login, and link to
the blog post and Privacy Notice.
- Changed the modal CTA to “Continue”.
- Updated the English and English (UK) locale catalogs and related
unit-test expectations.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Build and launch the extension with the Basic Functionality
consolidation feature enabled.
2. Use an SRP wallet that has a pending Basic Functionality migration
toast.
3. Verify the toast says the privacy toggles are now one Basic
Functionality setting, reflects whether it was turned on or off based on
previous settings, and includes the Settings > Privacy link.
4. Open the toast’s Settings > Privacy link and verify it navigates to
Privacy settings.
5. Use a social-login wallet with a pending migration modal.
6. Verify the modal explains that the sign-in email or Telegram handle
is saved with wallet addresses, says Basic Functionality stays on,
includes the blog post and Privacy Notice links, and has a Continue
button.
7. Click Continue and verify the modal closes.

## **Screenshots/Recordings**

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

### **Before**

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

Not applicable: this PR updates copy in existing UI surfaces.

### **After**

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

Not applicable: this PR updates copy in existing UI surfaces.

## **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_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Copy, localization, and a non-functional external link in migration UI
only; no changes to privacy logic or data handling.
> 
> **Overview**
> Updates **Basic Functionality migration** user-facing copy to match
consolidated privacy settings and social-login behavior.
> 
> The **migration toast** now explains that former privacy toggles are a
single **Basic Functionality** setting, still reflects on/off from prior
preferences, and adds a **Learn more** link (blog post) as a third
placeholder in `basicFunctionalityMigrationToastDescription`. The
**social-login modal** copy is rewritten around Google/Apple/Telegram
profile linkage, that Basic Functionality stays on for those accounts,
and shorter links to the blog and Privacy Notice; its title and general
modal title strings change, and the primary action label switches from
**Accept and close** to **Continue**.
> 
> **English** and **en_GB** catalogs carry the full message updates plus
the new `basicFunctionalityMigrationToastLearnMore` key; other locale
files mainly extend the toast string **description** to document the new
`$3` placeholder. Tests assert the blog link and updated button wording.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b34425a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…of 3) (#46149)

## **Description**

Per MetaMask content guidelines: UI strings shouldn't ask for favors.
Use direct imperative language instead.

Updated strings in `app/_locales/en/messages.json`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A
## **Manual testing steps**

1. Load the extension in a browser.
2. Navigate to any screen that displays the updated strings.
3. Confirm the updated wording appears correctly with no layout
regressions.

## **Screenshots/Recordings**

### **Before**

N/A — locale string changes only.

### **After**

N/A — locale string changes only.

## **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**
> Copy-only locale updates with no behavioral or security impact; minor
UX wording changes in error and confirmation surfaces.
> 
> **Overview**
> Updates **English UI copy** in `app/_locales/en/messages.json` and
`app/_locales/en_GB/messages.json` to follow MetaMask content
guidelines: drop favor-asking phrasing in favor of direct imperatives.
> 
> Across notifications, onboarding, perps, ramps, rewards, security, and
Shield flows, **"Please try again"** (and similar) becomes **"Try
again"** or shorter variants (e.g. **"Wait and try again"**). Validation
hints drop **"Please"** (**"Enter a valid URL"**, chain ID, email).
Other tweaks include **`pleaseConfirm`** → **"Confirm"**, tighter
error/retry lines, and a straight apostrophe in
**`rampsServiceDisruptionDescription`**.
> 
> No application logic or message keys change—only displayed strings for
those locales.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a59cdc9. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…of 3) (#46150)

## **Description**

Per MetaMask content guidelines: UI strings shouldn't ask for favors.
Use direct imperative language instead.

Updated strings in `app/_locales/en/messages.json`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A
## **Manual testing steps**

1. Load the extension in a browser.
2. Navigate to any screen that displays the updated strings.
3. Confirm the updated wording appears correctly with no layout
regressions.

## **Screenshots/Recordings**

### **Before**

N/A — locale string changes only.

### **After**

N/A — locale string changes only.

## **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**
> Copy-only localization updates with no behavioral or security impact;
minor UX wording change in error and guidance strings.
> 
> **Overview**
> Aligns **English (`en`) and British English (`en_GB`)** copy with
MetaMask content guidelines by replacing polite “please …” phrasing with
**direct imperatives** across Transaction Shield claims/coverage,
swap/smart-tx errors, Snaps privacy text, notifications, unlock, Trezor,
terms-of-use, and related UI strings.
> 
> Examples of the pattern: validation hints like “Please enter …” become
“Enter …”; retry/support lines drop “Please” (e.g. “Try again later”,
“reach out to $1”); payment-pause and reimbursement warnings use “Update
…” / “Contact support …” instead of “Please update/contact …”.
**`unlockPageIncorrectPassword`** is reworded to “Password is incorrect.
Try again.”
> 
> No code or logic changes—only **`message` text** in the two locale
JSON files.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5622820. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…6151)

## **Description**

Per MetaMask content guidelines: "successfully" is redundant — the
success state is implied.

Updated strings in `app/_locales/en/messages.json`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A
## **Manual testing steps**

1. Load the extension in a browser.
2. Navigate to any screen that displays the updated strings.
3. Confirm the updated wording appears correctly with no layout
regressions.

## **Screenshots/Recordings**

### **Before**

N/A — locale string changes only.

### **After**

N/A — locale string changes only.

## **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**
> Copy-only locale edits with no logic or security impact; minor UX
wording change in English locales only.
> 
> **Overview**
> Updates **English (`en`) and British English (`en_GB`)** locale copy
so success toasts and confirmations no longer say **"successfully"**,
per MetaMask content guidelines (success is already implied by the UI
state).
> 
> Wording changes cover dapp disconnect flows, hardware wallet
reconnect, token/network/NFT add-edit-remove, token import, generic
result pages, and Perps TP/SL update toasts—e.g. `"All dapps
successfully disconnected."` → `"All dapps disconnected."` and similar
shortenings. **No message keys or code paths change**, only the
`message` strings in `app/_locales/en/messages.json` and
`app/_locales/en_GB/messages.json`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
90df98e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…e str… (#46645)

…ings (#46151)"

This reverts commit ee33105. That PR
broke our e2e tests, leading to a blocked ci.

Reverting the PR, so the PR owners can fix the tests within their
changes

<!--
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?
-->

## **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]
> **Low Risk**
> English locale copy only with no logic, API, or behavior changes.
> 
> **Overview**
> Reverts the prior copy change that stripped **"successfully"** from
several extension success messages in
**`app/_locales/en/messages.json`** and
**`app/_locales/en_GB/messages.json`**.
> 
> Affected user-facing strings include dapp disconnect confirmations,
hardware wallet reconnect success, token/network/NFT add-edit-import
toasts, NFT removal, generic result-page success, and Perps TP/SL update
success. Wording is updated only; message keys and placeholders (e.g.
`$1`) are unchanged.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
42e3304. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ngs (… (#46660)

…batch 3 of 3) (#46150)"

This reverts commit 54bf455.

<!--
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?
-->

## **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]
> **Low Risk**
> User-visible string changes only in locale JSON; no application logic,
auth, or data handling is modified.
> 
> **Overview**
> Reverts the prior copy edit that stripped **"Please"** from many
user-facing strings, updating **`app/_locales/en/messages.json`** and
**`app/_locales/en_GB/messages.json`** in parallel so both English
variants stay aligned.
> 
> The touched copy spans **Transaction Shield** (claims validation,
coverage paused alerts, membership payment tooltips), **Snaps** install
privacy text, **state log** errors, **swap** failure/expired-quote
messaging, **Terms of Use** footer, **token search** errors, **Trezor**
desktop setup, **MetaMask notifications** enable/disable errors, **watch
Ethereum accounts** beta copy, and the **window.web3** shim
notice—mostly by prefixing imperatives with "Please" or softening
follow-up instructions (e.g. "Contact support" → "Please contact support
if you need…").
> 
> Separately, **`unlockPageIncorrectPassword`** is reworded from
"Password is incorrect. Try again." to **"Incorrect password. Try
again."**
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52fcb04. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ngs (… (#46663)

…batch 2 of 3) (#46149)"

This reverts commit dab5f03.

<!--
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?
-->

## **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]
> **Low Risk**
> Copy-only locale updates with no functional or security impact; risk
is limited to UI string length or tone consistency in en/en_GB.
> 
> **Overview**
> Reverts the prior content change that stripped **"please"** from many
extension strings, restoring more polite copy in
**`app/_locales/en/messages.json`** and
**`app/_locales/en_GB/messages.json`** only.
> 
> Updates are wording-only across error/retry toasts, validation hints,
and empty-state guidance—e.g. **"Try again" → "Please try again"**,
**"Enter a valid URL" → "Please enter a valid URL"**, **"Confirm" →
"Please confirm"**—touching areas such as notifications, onboarding IPFS
validation, Perps, ramps, rewards, security password errors, and Shield
claim flows. No logic or key renames; only `message` text changes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
725d70e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…46592)

<!--
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**
Converts the remaining legacy JavaScript migrations to TypeScript as
part of the incremental TS migration effort.

Runtime behavior is unchanged. Existing quirks are preserved
deliberately, including migration 017's legacy boolean-to-status
comparison and migration 026's rejection on error.

<!--
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#7370

## **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 run on every persisted-state upgrade via the migration loader
and touch vault, preferences, network, and transaction data, but the
diff is primarily typing and test hardening with stated runtime parity.
> 
> **Overview**
> This PR **TypeScript-converts** the early legacy migration stack (002
through 026), the shared **`fail-tx`** helper, and the migration
**loader** (`index.ts`), without intending to change upgrade behavior.
> 
> Each migration now imports **`LegacyMigration`** /
**`MigrationState`** from the migrator, uses local **`LegacyState`**
(and related) types for the old on-disk shapes, and exports with
**`satisfies LegacyMigration`**. Catch blocks use **`(err as
Error).stack`** instead of untyped `err.stack`. Known legacy quirks stay
explicit (e.g. migration **017** keeps its boolean/status comparison via
**`@ts-expect-error`**; migration **026** still **rejects** on transform
errors).
> 
> The loader is renamed from **`index.js`** to **`index.ts`**, typed as
**`Migration[]`**, and still loads migrations via static **`require()`**
for interop. **AI PR analyzer** critical-file config and the TS
migration dashboard list are updated to match the new paths.
> 
> Migration **tests** (021–026, **`migrations.test.ts`**,
**`023.test`**) gain typed fixtures, **`MigrationInput`** aliases, and
stricter assertions (e.g. **`toBeUndefined()`** / **`toBeDefined()`**).
Deleted **`.js`** counterparts include **002**, **026**, **`fail-tx`**,
and **`023.test.js`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a6c0003. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Recreates the non-session-sharing improvements of the upstream PR #45713
without the shared-Chrome-session infrastructure (see WPN-1881). Part of
a series of parallel, self-contained Tron E2E PRs (#46113–#46118).

- Adds
`tests/account-activity/tron/{tron-activity-status,tron-activity-types}.spec.ts`
— first E2E coverage for Tron activity history (pending/failed states;
approve/send/receive/swap/bridge presentations).
- Adds `tron-activity.flow` (`landOnTronActivityHome`,
`openTronTransactionDetails` with a route-close wait for the details
back button), `ActivityTab.clickActivityByText` /
`checkTransactionActivityNotPresentByText`, and transaction-details
helpers.
- Adds `FixtureBuilderV2.withBridgeStatusController` and a
`tronBridgeHistoryItem` fixture so bridge txs are reclassified via
`BridgeStatusController.txHistory` keyed by TronGrid `txID`.
- Includes an EVM-activity filter case (EVM send hidden under Tron-only
filter, visible under all-networks) via a dynamic
`v4/multiaccount/transactions` mock; fixture transactions live in
`transactions.raw` with `valueTransfers[].amount` in base units so
amounts render correctly.
- Includes shared Tron helpers this suite needs (readiness-based
`selectTronNetwork` in `tron-network.flow`, select-network-modal
`closeIfOpen`); these are intentionally duplicated across the sibling
PRs (byte-identical copies).

Local verification: `yarn build:test` + `test:e2e:single` on chrome —
1/1 and 2/2 passing.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

Part of WPN-1881. Parallel sibling PRs: #46113 (network), #46114
(account), #46115 (assets), #46116 (send), #46117 (swap). Recreates
#45713 without session sharing.

## **Manual testing steps**

1. Check out the branch, `yarn install && yarn webpack:tsc && yarn
build:test`.
2. `node test/e2e/run-e2e-test.js --browser chrome
test/e2e/tests/account-activity/tron/tron-activity-status.spec.ts`
(expect 1 passing) and `...
test/e2e/tests/account-activity/tron/tron-activity-types.spec.ts`
(expect 2 passing).

## **Screenshots/Recordings**

<!-- Not applicable: new E2E coverage with no user-facing UI change. -->

## **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.
…ion and improve error handling (#46340)

## **Description**

When the extension holds no WebHID grant for a Ledger device, the DMK
transport's `listenToAvailableDevices()` never emits, so discovery only
failed after `LEDGER_DEVICE_DISCOVERY_TIMEOUT_MS` (15s). An empty
`navigator.hid.getDevices()` result is definitive, so there is nothing
to wait for.

- **Fast-fail discovery** — `#assertPermittedDevicePresent()` probes
`getDevices()` first and rejects immediately when no Ledger device is
present.
- **Shared error** — `createNoPermittedDeviceError()` is now used by
both the fast-fail probe and the timeout path in
`normalizeDiscoveryError`, so both emit identical copy and
classification.
- **`requestWebHidDevices()`** — new grant helper returning devices
filtered by wallet type, with `requestWebHidPermission()` as a boolean
wrapper. Fixes a latent bug where `ledger-info.tsx` compared
`device.vendorId` (number) to `LEDGER_USB_VENDOR_ID` (the string
`'0x2c97'`).
- **De-duplication** — `ledger-info.tsx` and `connectHardware()` now
share the helper and `isInE2eTest()` instead of inlining their own
`requestDevice` call and E2E check.
- **App-closed errors surface as errors** —
`getConnectionStateFromError` maps `DeviceStateEthAppClosed` to the
error state, consistent with the rest of the switch. The awaiting-app UX
is unchanged, driven by `DeviceEvent.AppNotOpen`.

## **Changelog**

CHANGELOG entry: Improved Ledger connection feedback by failing fast
with a clear error when no permitted Ledger device is found, instead of
waiting for the discovery timeout.

## **Related issues**

Fixes:  https://consensyssoftware.atlassian.net/browse/MUL-2206
Related: https://consensyssoftware.atlassian.net/browse/MUL-2221
Related: https://consensyssoftware.atlassian.net/browse/MUL-2230

## **Manual testing steps**

1. Run the extension and load `dist/chrome` as an unpacked extension.
2. Connect a Ledger with a WebHID grant in place — verify connecting
still succeeds.
3. Revoke the grant (`chrome://settings/content/hidDevices`, or a fresh
profile).
4. Start the Ledger connection flow from a confirmation's Ledger info
step.
5. Verify "No permitted Ledger device found" appears promptly (not after
~15s) in the error state.
6. Verify the device picker still opens and lists Ledger devices when no
grant exists.
7. With the Ledger on the dashboard, trigger an Ethereum-app transaction
and verify the
   app-closed state is shown as an error.

## **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
- [ ] 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 Ledger connection timing, WebHID grant entry points, and
error-to-UI mapping during hardware signing flows (including automatic
send-bundle retries), where regressions would block or confuse device
signing.
> 
> **Overview**
> Ledger DMK discovery now **fails immediately** when
`navigator.hid.getDevices()` shows no permitted Ledger device, instead
of waiting for the discovery timeout. The offscreen handler shares
**`createNoPermittedDeviceError()`** with the timeout path so both
surface the same **"No permitted Ledger device found"** error.
> 
> WebHID permission is centralized in **`requestWebHidDevices()`** (with
**`requestWebHidPermission()`** as a boolean wrapper), replacing
duplicated `requestDevice` logic in **`ledger-info`**,
**`connectHardware`**, and related tests— including a fix for comparing
**`vendorId`** to the string **`LEDGER_USB_VENDOR_ID`**. E2E detection
uses shared **`isInE2eTest()`**.
> 
> Hardware-wallet **connection errors** no longer go through
**`getConnectionStateFromError`**; structured errors always become
**`ConnectionState.error(error)`**. For **swap/send signing**,
**`DeviceStateEthAppClosed`** no longer drives failed/disconnected
signature states: **`getHardwareWalletSignatureErrorEvent`** returns
**`null`**, connection monitoring ignores it, and **send-bundle** flows
**auto-restart** after **`ensureDeviceReady()`** via **`handleRetry({
skipConnectionCheck: true })`** when the Ethereum app was closed
mid-submit.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
eb65be7. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Adds a Chrome-first exact storage-byte measurement path for sampled
split-state write telemetry.

**Reason:** The existing follow-up PR reduces measurement cost by
estimating controller size with `JSON.stringify(value).length`. This PR
explores another approach using `getBytesInUse` so sampled writes can
report actual extension-storage quota bytes when the browser supports
it.

**Solution:**
- Add an optional `BaseStore.getBytesInUseByKey(keys)` capability.
- Implement that capability in `ExtensionStore` using one
`browser.storage.local.getBytesInUse(key)` call per controller key.
- After a successful sampled persist, await per-key storage-byte
measurement inside the existing exclusive persist lock.
- Fall back to the string-length estimate when `getBytesInUse` is
unavailable or fails.
- Emit `sizeMeasurementSource` so Sentry can distinguish exact storage
bytes from fallback estimates.

This is intentionally stacked on top of #46423.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Related: #46423
Related: #44812

## **Manual testing steps**

1. Ensure `.metamaskrc` has `SENTRY_DSN_DEV` set and
`MANIFEST_OVERRIDES=.manifest-overrides.json`.
2. Set `_flags.remoteFeatureFlags.sentry.persistenceWriteSampleRate` to
`1` in `.manifest-overrides.json`.
3. Run `yarn start --sentry`, load the extension, and opt in to
MetaMetrics.
4. Trigger a split-state write, for example by unlocking and changing a
wallet setting.
5. In Sentry test-metamask, open `State Persist` traces.
6. Confirm spans include `state.write.size_measurement_source`.
7. On Chrome, confirm the value is `storage_get_bytes_in_use` and byte
attributes are present.
8. On unsupported browsers, confirm telemetry still works with
`json_string_length_estimate`.

## **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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes affect sampled telemetry only after successful persists, with
explicit fallbacks and tests; persistence success path is guarded so
measurement errors cannot fail writes.
> 
> **Overview**
> Sampled **split-state write telemetry** can now report **actual
extension-storage byte counts** (Chrome `storage.local.getBytesInUse`)
instead of relying only on `JSON.stringify` length estimates.
> 
> **PersistenceManager** awaits optional `getBytesInUseByKey` after a
successful sampled persist (still inside the write lock), sums
per-controller bytes, and emits **`sizeMeasurementSource`**
(`storage_get_bytes_in_use` vs `json_string_length_estimate`).
Measurement failures or missing APIs fall back to the JSON estimate
without failing the write. **`SplitStateWriteEvent`** drops redundant
**`controllerKeys`**; controller identity comes from
**`bytesByController`**.
> 
> **ExtensionStore** implements **`getBytesInUseByKey`** on
**`BaseStore`**. **Sentry** spans gain
**`state.write.size_measurement_source`**; controller count/list
attributes are derived from the byte map.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e644a2f. 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: Claude Sonnet 4.6 <noreply@anthropic.com>
## **Description**

`Swap Quote Fetch` (added in #45667) was missing from
`DEFAULT_TRANSACTION_SAMPLE_RATES`, so the `tracesSampler` resolved it
through its generic fallbacks instead of a pinned per-name rate.

The consequence was not just "inherits the global rate".
`getTransactionSampleRate` resolves an unlisted name through the
`parentSampled` branch, which returns `1` on a positive parent decision:

```typescript
if (typeof parentSampled === 'boolean') {
  return parentSampled ? Math.min(1, ceiling) : 0;
}
```

`trace()` marks a span started under an active parent as
`forceTransaction` (`shared/lib/trace.ts`), and `forceTransaction`
causes Sentry to re-consult the sampler — which the repo's integration
test already asserts ("re-consult the sampler for forced transactions...
so a throttled name is dropped even nested under a sampled parent"). So
a quote fetch nested under a sampled parent (e.g. a `pageload` span)
could be recorded at full rate rather than the global ~0.5%.

**Solution:** register the name in `DEFAULT_TRANSACTION_SAMPLE_RATES`
with a pinned `0.001` starting rate, matching the Perps precedent. A
listed name stops at tier 2 of the sampler and never reaches the
`parentSampled` branch, so its rate is deterministic and
nesting-independent. It also becomes tunable via the existing remote
`sentry.transactionSampleRates` flag.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: null

## **Manual testing steps**

1. Load the extension and note the Sentry sample rate (default `0.005`;
`tracesSampleRate: 1` in `.manifest-overrides.json` makes the outcome
deterministic).
2. Navigate to the unified Swaps/Bridge view and request a quote.
3. Confirm `Swap Quote Fetch` spans still emit and carry the correct
`op:bridge.data_fetch` and `result` attribute.
4. Confirm the interval between recorded `Swap Quote Fetch` spans widens
to roughly 1-in-1000 (or tune `sentry.transactionSampleRates` to `1` for
the name and confirm they all emit).

Unit coverage that pins the behavior:

```bash
yarn jest app/scripts/lib/sentry-traces-sampler.test.ts app/scripts/lib/sentry-traces-sampler.integration.test.ts --no-coverage
```

<!--
## **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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes only Sentry trace sampling configuration and tests; no product
logic, auth, or data handling.
> 
> **Overview**
> **Swap Quote Fetch** Sentry transactions are now pinned to a **0.1%**
(`0.001`) per-name sample rate via `SWAP_QUOTE_FETCH_SAMPLE_RATE` and an
entry in `DEFAULT_TRANSACTION_SAMPLE_RATES`, matching the existing Perps
high-volume pattern.
> 
> Previously this name was unlisted, so nested quote fetches under a
sampled parent could hit the `parentSampled` path and be recorded at
**full rate** (debounced typing + `forceTransaction` re-sampling). The
pinned rate applies even when `parentSampled: true`, and the name can
still be tuned through remote `sentry.transactionSampleRates`. Unit
coverage asserts both behaviors.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b9cdb68. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

This investigation started with the impact of
[EIP-2780](https://eips.ethereum.org/EIPS/eip-2780) on Smart Transaction
cancellation variants. EIP-2780 replaces the flat intrinsic transaction
cost, so the cancellation path's hardcoded 21,000 gas limit needs to go:
a self-transfer may have a different intrinsic cost, and delegated
senders can incur additional runtime work. While tracing that path, we
found that Extension cannot execute it at all. Rather than redesigning
gas estimation for unreachable behavior, this PR removes the dead
cancellation variant altogether.

Smart Transaction publication retained a cancellation-aware branch that
could rewrite a transaction into a self-transfer with empty data and
force its gas limit to 21,000. The branch was unreachable: the publish
hook had one fee-signing call, that call always passed `false`, and the
resulting submission always supplied an empty
`signedCanceledTransactions` array.

This PR therefore:

- removes `isCancel` from `#createSignedTransactions` and
`#applyFeeToTransaction`;
- removes the hardcoded 21,000 gas selection and the `to = from` / `data
= '0x'` mutation;
- removes the now-unused `CANCEL_GAS_LIMIT_DEC` constant;
- omits `signedCanceledTransactions` so the Smart Transactions
controller uses its optional parameter default;
- preserves pre-signed, fee-signed, and batch publication behavior;
- updates `@metamask/transaction-controller` from `^69.8.0` to
`^70.1.0`; and
- uses LavaMoat policies generated by `@metamaskbot update-policies` on
the CI operating system, avoiding platform-dependent macOS policy
output.

The controller upgrade resolves to 70.1.0, whose package is ESM,
declares Node `^22.14.0 || ^24`, and exposes its compiled entry point
and declarations through package exports. Existing Jest partial mocks
were updated to preserve the package's live export property descriptors
instead of spreading and eagerly evaluating them during circular module
initialization. Extension TypeScript and focused Jest validation pass
with it; MetaMask Bot generated the final Linux LavaMoat policies in
[`fb7a815`](fb7a815).

### **Why the removed code is verifiably dead without manual testing**

The dead-code conclusion follows from the local call graph and data
flow, rather than from an inability to reproduce the behavior manually:

1. `#createSignedTransactions` was private and had exactly one call site
in `SmartTransactionHook`.
2. That call site always passed the literal `false` as `isCancel`.
3. `#createSignedTransactions` only forwarded that value to the private
`#applyFeeToTransaction` helper.
4. Every cancellation-specific operation—forcing gas to 21,000, setting
`to` to `from`, and emptying `data`—was guarded by `isCancel`.
5. No path read `tradeTxFees.cancelFees`; every submission constructed
`signedCanceledTransactions` as the literal empty array.

Because private methods have no external call surface and the only
incoming value was a constant `false`, the cancellation branch could not
execute for pre-signed, fee-signed, or batch publication. This is
statically inspectable and does not depend on relay behavior, feature
flags, timing, or network state.

The focused regression test strengthens that proof: its fee response now
contains non-empty `cancelFees`, yet it asserts that
`approveTransactionsWithSameNonce` is invoked exactly once with only the
trade transaction. Submission tests for pre-signed, fee-signed, and
batch paths also assert that `signedCanceledTransactions` is absent.

### **Git history analysis**

The history distinguishes when the cancellation behavior was introduced
from when it became dead:

- The cancellation signing implementation first appears in draft
publish-hook commit
[`143053e`](143053e)
on January 22, 2024. It actively signed both `tradeTxFees.fees` with
`isCancel = false` and `tradeTxFees.cancelFees` with `isCancel = true`.
- The behavior reached the main product path in
[#23155](#23155),
merged as
[`f5ef4c7`](f5ef4c7)
on April 10, 2024. That change introduced `CANCEL_GAS_LIMIT_DEC` and
retained the active cancellation-fee signing call.
- The branch became dead in
[#26251](#26251),
merged as
[`590f0a9`](590f0a9)
on August 21, 2024. To support hardware-wallet/pre-signed publication
and "sign a transaction only once," that change removed the `cancelFees`
signing call and replaced its output with `signedCanceledTransactions:
[]`, but left the `isCancel` helper branch and 21,000 constant behind.

This PR removes the residue left by #26251; it does not remove behavior
that has remained reachable since #26251 merged.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

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

Related client implementation: [MetaMask Mobile
#36487](MetaMask/metamask-mobile#36487)

Related:
[CONF-1998](https://consensyssoftware.atlassian.net/browse/CONF-1998),
[CONF-2002](https://consensyssoftware.atlassian.net/browse/CONF-2002)

Blocks:
[CONF-2003](https://consensyssoftware.atlassian.net/browse/CONF-2003)

## **Automated verification**

- `yarn install` — passed with existing peer-dependency warnings.
- `yarn lint:lockfile:dedupe:fix` — passed; existing 70.x ranges
deduplicated to transaction-controller 70.1.0.
- `yarn allow-scripts auto` — passed; no configuration changes required.
- Local `yarn lavamoat:auto` and `yarn build:test` validation passed
before rebase, but the platform-dependent macOS policy output was
intentionally discarded; MetaMask Bot generated and committed the final
policies on Linux CI.
- Final Linux CI LavaMoat validation — webpack build, MV2, and MV3
policy checks passed after the bot commit.
- Final post-rebase Smart Transaction and transaction-controller
partial-mock validation — 5 suites and 127 tests passed.
- `yarn lint:tsc` — passed after the ESM mock compatibility update.
- `yarn lint:changed:fix` — passed.
- `yarn lint:tsc` — passed.
- `yarn lint:json` — passed.
- `yarn lint:lockfile` and `yarn lint:lockfile:dedupe` — passed.
- `yarn circular-deps:check` — passed.
- Attributions were regenerated for both the original and updated
dependency graphs; the generated outputs were identical, so this package
update requires no `attribution.txt` change.

## **Manual testing steps**

1. Run the Extension with Smart Transactions enabled and unlock a
software wallet on a supported network.
2. Submit a Smart Transaction that requires MetaMask to apply
relay-provided fees, then verify it is published and receives a
transaction hash.
3. Submit a pre-signed Smart Transaction, such as through the applicable
hardware-wallet flow, and verify it is published without a behavior
change.
4. Submit a supported transaction batch from a batch-capable test dapp
and verify each transaction receives its expected result.
5. While inspecting the relay submission, verify only trade transactions
are signed and submitted: the original gas, recipient, and data are
preserved, and no cancellation transaction array is sent.

<!--
## **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 (no new public API)
- [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-1991]:
https://consensyssoftware.atlassian.net/browse/CONF-1991?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Removes statically unreachable cancellation code; observable publish
behavior for trade, pre-signed, and batch Smart Transactions stays the
same.
> 
> **Overview**
> Removes unreachable Smart Transaction **cancellation** signing from
the Extension publish hook. Fee application no longer branches on
`isCancel`, does not force a 21,000 gas self-transfer with empty `data`,
and drops the unused `CANCEL_GAS_LIMIT_DEC` constant.
> 
> Submissions now omit `signedCanceledTransactions` so the controller
relies on its optional default; **trade-only** signing via
`tradeTxFees.fees` is unchanged for pre-signed, fee-signed, and batch
paths.
> 
> Tests were tightened to use non-empty `cancelFees` in mocks and assert
`approveTransactionsWithSameNonce` runs once and submit payloads never
include `signedCanceledTransactions`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d804ca6. 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**

<!--
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:

## **Manual testing steps**

No manual testing steps

## **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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Messenger changes expand account-tree and snap-account access to
account listing/removal and keyring removal paths; incorrect delegation
could affect wallet/account lifecycle, though changes are permission
wiring aligned with upstream controllers.
> 
> **Overview**
> This PR updates **accounts-related controller packages** and wires the
extension to their new messenger and dependency requirements.
> 
> **Account tree and snap account messengers** grant additional
delegated actions/events required by the bumped
`@metamask/account-tree-controller` and `@metamask/snap-account-service`
versions— including listing accounts (`AccountsController:getAccounts` /
`getState`), removing wallets and keyring accounts
(`MultichainAccountService:removeMultichainAccountWallet`,
`KeyringController:removeAccount`), and reacting to
`AccountsController:accountsAdded` / `accountsRemoved`.
> 
> **Rewards authentication** adds an explicit TypeScript cast on the
`siwe` argument passed to `KeyringController:signPersonalMessage`,
working around a temporary type mismatch between older `detectSIWE` from
`@metamask/controller-utils` and the keyring controller’s SIWE types
until `controller-utils` is upgraded.
> 
> **LavaMoat webpack policies** (all MV2/MV3 variants) are regenerated
to match the new nested package graph— notably scoped
`@metamask/keyring-controller` and `@metamask/utils` entries,
`console.error` for account-tree-controller, and related allowance
tweaks across controllers that depend on the bumped stack.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4c6824c. 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>
…irectly by URL (#46547)

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

Opening a MetaMask page as the first history entry (new window, pasted
URL, bookmark) made the in-app Back arrow call navigate(-1) and leave
the extension. This is the same issue as #44590, but it also showed up
on Money, Send, Asset, Activity details. Perps is also affected but I
will make changes separately as they are more complex.

`useInAppBack` now treats React Router’s `location.key === 'default'` or
`state.fromFreshTab` as “no in-app history.” In that case the in-app
Back button replaces the current entry with an explicit in-app fallback.
If there is in-app history, it still goes back one step. Browser Back is
unchanged.

Fallbacks:

- Money (activity, earn, how-it-works, transaction details) → Money home
- Send / Asset → wallet home
- Activity details → activity home

## **Changelog**

CHANGELOG entry: Fixed the back arrow navigating outside of MetaMask
when pages are opened directly from a URL

## **Related issues**

Fixes: #46496

## **Manual testing steps**

### Money
1. Open the browser and go to any page
2. Paste a Money activity, earn, how-it-works, and a Money transaction
details URL, e.g. "#/money-home/earn"
3. Tap in-app Back on each. Confirm you land on Money home, still inside
MetaMask.
4. From Money home, open the same screens in-app, then Back. Confirm you
return to the previous Money/home screen.

### Assets
1. Open the browser and go to any page
2. Paste a deep link, e.g.
https://link.metamask.io/asset?assetId=eip155:1/erc20:0x6B175474E89094C44Da98b954EedeAC495271d0F
(continue through the interstitial if shown)
3. On the asset page, click the back arrow
4. Verify you land on the MetaMask home page instead of the previous
website
5. Regression check: open MetaMask normally, click a token in the list,
press the back arrow — verify you return to the token list as before

### Send
1. Open the browser and go to any page
2. Paste the Send flow URL.
3. Tap in-app Back. Confirm you land on wallet home.
4. Start Send from home, then Back. Confirm you return to the previous
in-app screen.

### Activity details
1. Open the browser and go to any page
2. Paste a generic transaction details URL (/tx-details/... or
equivalent).
3. Tap in-app Back.
4. Bottom nav on: confirm you land on /activity.
5. Bottom nav off: confirm you land on home with the Activity tab
(/?tab=activity).
6. Open the same details from Activity, then Back. Confirm you return to
the activity list/tab you came from.

## **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.

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-13eec30d-e9d2-42c9-ac02-cea6bf80d43a?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-13eec30d-e9d2-42c9-ac02-cea6bf80d43a&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>
…led to fetch account'` (#46670)

<!--
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 console error also appears in the Edit Accounts test,

WE have a bug ticket opened here:
#46623

Temporarily ignore the error like in this other PR
#46624

<!--
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. Ci should pass

## **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**
> Test-only change that suppresses a known console error during e2e; no
production behavior is modified.
> 
> **Overview**
> The **Edit Accounts Permissions** e2e test now passes
`ignoredConsoleErrors` into `withFixtures` so a known
**`[PerpsStreamManager] Failed to fetch account`** console message does
not fail the run.
> 
> This mirrors the temporary workaround from PR #46624 while **#46623**
tracks the underlying issue; a short comment in the spec points
reviewers to that ticket.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c2885ac. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…3.50.0 (#46641)

<!--
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 social-login Basic Functionality migration modal showed two body
blocks with a large gap: the explanation, then “Read more in our blog
post and Privacy Notice.”

This renders those two existing strings as one paragraph so the layout
matches the intended notice design. Copy and links are unchanged.

## **Changelog**

CHANGELOG entry: Combined the social login Basic Functionality update
notice into a single paragraph.

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

Fixes:
-->

## **Manual testing steps**

1. Run the extension and unlock a **social login** wallet (Google,
Apple, or Telegram).
2. Trigger the Basic Functionality social migration modal (consolidation
notice scheduled as modal).
3. Confirm the explanation and “Read more in our blog post and Privacy
Notice.” appear as **one** paragraph, not two stacked blocks.
4. Confirm **blog post** and **Privacy Notice** still open the correct
URLs.
5. Confirm **Continue** still dismisses the modal.
6. Confirm an SRP wallet still gets the toast, not this modal.

## **Screenshots/Recordings**

### **Before**

Two body paragraphs with a gap between the explanation and the “Read
more…” line.

### **After**

<img width="712" height="792" alt="Screenshot 2026-09-25 at 8 38 02 AM"
src="https://github.com/user-attachments/assets/e851cb5b-ef59-4f4b-bd5f-40d587d281c9"
/>

## **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.

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**

The Buy amount screen header showed **Buy MUSD** instead of **Buy
mUSD**. The title is built by interpolating the token symbol from the
ramps catalog into the `rampsBuyToken` locale string ("Buy $1"), and the
catalog reports the uppercase registry symbol `MUSD` — while the
on-chain token (verified via `symbol()`) and every locale string in the
product use the branded casing `mUSD`.

This PR canonicalizes the symbol through `getTokenDisplaySymbol` from
`@metamask/money-account-utils` — the shared single source of truth that
already exists for exactly this registry-vs-branded casing problem — at
every Buy flow display site (build-quote header, complete-buy handoff,
payment-method labels, provider-selection quotes, token-selection rows).
It also replaces the stale duplicate `MUSD_TOKEN` constant in
`ui/components/app/musd/constants.ts` with a re-export from the
canonical package, correcting activity rows, the confirmation hero, and
money transaction details displays as well.

Note: `trackTokenSelected` analytics now report `mUSD` for mUSD buys
(previously `MUSD`), matching what users see. Events still carry
`token_caip19`, so dashboards can key by address. A backend fix to
return the branded casing from the token catalog is tracked separately
([TRAM-4050](https://consensyssoftware.atlassian.net/browse/TRAM-4050));
once it lands, the client-side canonicalization becomes a no-op.

## **Changelog**

CHANGELOG entry: Fixed a bug that was causing mUSD to appear as "MUSD"
in the Buy flow headers and labels

## **Related issues**

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

## **Manual testing steps**

1. Run the extension and unlock a wallet on Ethereum Mainnet or Linea.
2. Start the Buy flow for mUSD (token selection → amount screen).
3. Verify the top header on the Buy amount / Build Quote screen reads
"Buy mUSD" (previously "Buy MUSD").
4. Proceed to the payment method and provider selection screens and
verify quote labels show "mUSD".
5. Continue to the provider checkout and return to the completion
screen; verify the token symbol shows "mUSD".
6. Check the token selection list rows and verify mUSD appears as
"mUSD".


## **Screenshots/Recordings**

### **After**

<img width="429" height="854" alt="mUSD-casing"
src="https://github.com/user-attachments/assets/6cdd3d89-6817-4bfe-b234-ad8d27a48e5b"
/>


## **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-4050]:
https://consensyssoftware.atlassian.net/browse/TRAM-4050?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Display and constant sourcing changes only; behavior is covered by
unit tests and does not touch auth, transactions, or payment logic.
> 
> **Overview**
> Fixes **Buy MUSD** headers and labels when the ramps catalog returns
the uppercase registry symbol **MUSD** instead of the branded **mUSD**.
> 
> **mUSD metadata** now comes from `@metamask/money-account-utils`:
local `MUSD_TOKEN` in `ui/components/app/musd/constants.ts` is a
re-export (symbol **mUSD**, name **MetaMask USD**), so wallet import and
other mUSD UI stay aligned with on-chain branding.
> 
> **Ramps buy flow** routes catalog symbols through new
`getRampsTokenDisplaySymbol` in `ui/pages/ramps/utils/token-display.ts`,
which uses shared `getTokenDisplaySymbol` with the ERC-20 address parsed
from the CAIP-19 asset id. Build quote titles, complete-buy handoff,
payment-method quote labels, provider-selection quotes, and
token-selection list rows all use this helper; token mapping reuses the
same helper and replaces the local asset-id address parser with
`getErc20AddressFromAssetId`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
284c1ce. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#46685)

## **Description**

On the Buy token selection page, replaces the "Show all tokens" button
with scroll-to-bottom pagination.

- The list starts with the catalog's top tokens. Scrolling near the
bottom reveals 50 more tokens at a time (`ScrollNearBottom`, a small
helper rendered inside the existing `ScrollContainer`), until the full
catalog is visible.
- Lists too short to scroll keep revealing pages automatically so extra
tokens are never unreachable.
- Search and network-filter views are unchanged (full filtered list at
once).
- The info icon explaining disabled tokens was already delivered in
#46347 (TRAM-3710/TRAM-3961), so this PR only covers the scrolling half
of the ticket.

## **Changelog**

CHANGELOG entry: Replaced the "Show all tokens" button on the Buy token
selection page with scroll-to-bottom pagination

## **Related issues**

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


## **Manual testing steps**

1. Run the extension and open the Buy flow.
2. On the token selection page, confirm the list initially shows the top
tokens and the "Show all tokens" button is gone.
3. Scroll to the bottom of the list — verify roughly 50 more tokens
appear, and repeat until the full catalog is shown (scrolling further
adds nothing).
4. With a region/catalog whose top tokens don't fill the screen, verify
the list fills itself with more tokens automatically (no scrollbar
needed).
5. Enter a search term and apply a network filter — verify the full
filtered list shows at once and scrolling adds nothing.
6. Verify disabled (region-unsupported) tokens still show the info icon
and explanation modal.

## **Screenshots/Recordings**

### **After**


https://github.com/user-attachments/assets/2d6275c2-ac70-4da1-8c22-35dd4fb27c29


## **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-3962]:
https://consensyssoftware.atlassian.net/browse/TRAM-3962?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TRAM-3962]:
https://consensyssoftware.atlassian.net/browse/TRAM-3962?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only change to ramps token list loading with dedicated unit tests;
no auth, payments, or data-layer changes.
> 
> **Overview**
> On the Buy **token selection** screen, the footer **"Show all
tokens"** control is removed in favor of **scroll-to-load** behavior
inside the existing scroll area.
> 
> The list still opens with catalog **top tokens** only; scrolling
within ~200px of the bottom appends **50 deduplicated catalog tokens**
at a time via a new **`ScrollNearBottom`** helper on
**`ScrollContainer`**, until the full catalog is shown. If the first
page is too short to scroll, the helper keeps loading pages
automatically so tokens are not stuck behind a missing button.
> 
> **Search** and **network filter** modes are unchanged: they still
render the full filtered list immediately, with scroll pagination
disabled. The **`rampsShowAllTokens`** string is dropped from all locale
files, and tests/snapshots cover pagination, no-op scroll, search,
filters, and short-list auto-fill.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5091031. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

`applyPatches` in `ui/store/actions.ts` folds background state patches
into the Redux store. immer's patch support is a plugin you have to turn
on, and the registry is per copy of immer, so someone has to enable it
for the copy this file imports.

Nobody here ever did. It works by accident: we and the core packages
both want immer 9, so yarn dedupes them to one module and
`@metamask/base-controller` enables the plugin for us as a side effect.

Core is moving to immer 11 (MetaMask/core#10382). Once `base-controller`
ships with it, we get a second copy of immer that nobody enables, and
every background state update throws. Measured in #46595: **58 e2e jobs
red without this change, 64 green with it.**

## **Changelog**

CHANGELOG entry: null

## **Related issues**

MetaMask/core#10382
Tested by: #46595

## **Manual testing steps**

Normal CI covers current behaviour. For the failure this prevents, see
#46595.

## **Screenshots/Recordings**

N/A, no UI change.

## **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
- [ ] 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**
> Single module-load initialization with no intended behavior change
today; it hardens the critical background-to-Redux patch path ahead of
an immer version split.
> 
> **Overview**
> Calls **`enablePatches()`** in `ui/store/actions.ts` on the extension
UI’s own **`immer`** import, right before **`applyPatches`** runs during
**`updateMetamaskState`**.
> 
> Patch support in immer is opt-in and **per module instance**.
**`applyPatches`** already merges background **`Patch[]`** into Redux
**`metamask`** state; today that often works only because
**`@metamask/base-controller`** enables patches on a deduped shared
**`immer@9`**. When core moves to **`immer@11`** (MetaMask/core#10382),
the UI can get a second copy with patches never enabled, and **every
background state update throws**.
> 
> This makes patch application reliable for the UI regardless of how
dependencies dedupe immer.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5903dcc. 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.50.0
## **Description**

Users could complete social login and wallet creation, but fail to
restore the wallet on another device with:
```text
SeedlessOnboardingController - Primary secret data must be of type mnemonic.
```
The issue occurred when the primary SRP metadata was saved remotely but
OPRF share persistence failed. The SRP hash was already committed
locally, so a retry skipped the remote backup and left the server with
incomplete primary SRP data.
This PR:
- Upgrades `@metamask/seedless-onboarding-controller` from `^10.1.1` to
`^11.0.1`, while keeping `@metamask/wallet` at `14.0.1`.
- Fixes retry behavior for incomplete TOPRF backups.
- Keeps users on the create-password page when wallet setup fails.
- Displays a localized error message: “Something went wrong. Please try
again.”
- Clears the error when the user retries.
- Adds unit test coverage for the failure state.

## **Changelog**

CHANGELOG entry: Fixed an issue that could prevent social-login users
from restoring their wallet on a new device.

## **Related issues**

Fixes:
[#46631](#46631)

## **Manual testing steps**

1. Open MetaMask in a clean profile and start creating a new wallet
using social login.
2. Complete social login and navigate to the create-password page.
3. Use a test failure injection or block the seedless/Toprf backup
request, then submit a valid password.
- in network tab, block all the url matching
`https://node-[1-5].dev-node.web3auth.io/sss/jrpc` as shown in below
recording
4. Verify that the user remains on the create-password page and sees:
“Something went wrong. Please try again.”
5. Restore the backup request and submit again.
   - unblock all the network requests
6. Verify that the error clears and successful onboarding continues.
7. In another device (or browser), login with the same social login
email
8. Assert the successful rehydration with the same wallet restored.

## **Screenshots/Recordings**

### **Before**

<!-- Add a before screenshot/recording if available. -->

Silent error causes user's retry failed to backup the new encrypted SRP
metadata in the server.
Login to new device failed with **missing Primary SRP**, as it was
failed during the account creation.


https://github.com/user-attachments/assets/75387239-a3be-40f8-b58f-eb923825a5c3

### **After**

<!-- Add an after screenshot/recording showing the inline error state.
-->

Handle network errors and allow user retry to backup the new encrypted
SRP metadata to the server.


https://github.com/user-attachments/assets/7ae871cb-7574-4466-b56c-3f3fc0ad6b90


## **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 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. pulled and built the branch, ran
the app, and tested the changed flow).
- [ ] 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 seedless onboarding and backup retry paths plus
dependency/LavaMoat policy changes around crypto-related controllers,
though runtime behavior is mostly improved error handling and a
controller bugfix.
> 
> **Overview**
> Upgrades **`@metamask/seedless-onboarding-controller`** to
**`^11.0.1`** so incomplete TOPRF/OPRF backup failures can be retried
correctly instead of leaving remote primary SRP data in a bad state.
**LavaMoat** policies across MV2/MV3 build flavors are updated to match
the new dependency graph (direct
`@metamask/seedless-onboarding-controller` entry and scoped
`@metamask/utils` allowances).
> 
> On the **create-password** onboarding step, failed wallet setup no
longer silently bounces the flow: the page stays put,
**`CreatePasswordForm`** accepts an **`error`** prop and shows the
localized “Something went wrong. Please try again.” message, and the
error clears when the user submits again. Unit tests cover the new error
UI in the form and onboarding page.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
123f0c6. 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>
…cp-13.51.0 (#46668)

## **Description**

Money Account could become the pay method for a Perps deposit, and the
receive method for a Perps withdraw, without the Money ↔ Perps feature
flags being on. Because that route has no quote, fee, or settlement path
yet, the confirmation showed no fee or quote details before Confirm,
Perps withdraws to Money Account found no quotes, and a confirmed
deposit never debited the money account or credited the Perps account.

Two paths let it through:

1. `defaultPaySelectedSection` resolved a section for perps / predict
types from a shared `default` key, and from the sibling deposit ↔
withdraw key. A value served for the Money Account deposit rollout
therefore also made Money Account the default for Perps deposit and
withdraw. These directions roll out one at a time, so the confirmation
type now has to be named explicitly in the flag.

2. The `payWithOption=money_account` branch in
`useDefaultPaySelectedSection` applied the Money Account payment
override after checking only the transaction type — no flag check at
all. A restored URL or the developer-options button could lock a Perps
deposit to Money Account while
`enableMoneyAccountTransactions.perpsDeposit` was off, and `PayWithRow`
hides the token picker in that state, leaving no way to pick anything
else. That condition now lives in `useIsMoneyAccountPerpsNavigation`,
which also requires the flag, and every surface that presents the Money
Account → Perps flow reads it: the payment override, the pay-with row,
the Perps account picker row, the header title, and the footer label.

Both gates stay flag-driven, so setting
`confirmations_pay_extended.enableMoneyAccountTransactions` still
enables the flow for testing and for the eventual rollout.

## **Related issues**

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

## **Manual testing steps**

With the Money ↔ Perps flags off (no
`confirmations_pay_extended.enableMoneyAccountTransactions` entries for
perps):

1. Run the extension with an upgraded money account that holds a
balance.
2. Open a Perps deposit confirmation from the Perps UI.
3. Verify the pay-with row defaults to a crypto token, not Money
account.
4. Open the "Pay with" modal and verify there is no Money account row.
5. Open a Perps withdraw confirmation and verify the receive token
defaults to a crypto token, not Money account.
6. On Money Home, open the transfer sheet and verify the Perps account
row is disabled.

With `enableMoneyAccountTransactions: { "perpsDeposit": true,
"perpsWithdraw": true }` set in `.manifest-overrides.json`:

7. Repeat steps 2 and 4 and verify the Money account row appears in the
"Pay with" modal.
8. From Money Home → transfer sheet → Perps account, verify the deposit
opens with Money account locked as the source, the title reads "Send to
perps", and the footer button reads "Send".
9. Verify Money account only becomes the *default* when
`defaultPaySelectedSection` names the type explicitly (for example `{
"perpsDeposit": "money-account" }`); a `{ "default": "money-account" }`
entry alone must not default Perps to Money account.

<!--
## **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-2085]:
https://consensyssoftware.atlassian.net/browse/CONF-2085?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes payment defaults and confirmation UI for Perps/Money flows;
mis-flagging could hide pay options or skip Money Account overrides, but
behavior stays behind explicit remote feature flags.
> 
> **Overview**
> Money Account ↔ Perps was activating without the right remote flags,
leaving confirmations without quotes or a way to change funding source.
This PR **tightens two gates** and **centralizes** the Money Account →
Perps deposit UX behind one hook.
> 
> **`useIsMoneyAccountPerpsNavigation`** now requires
`payWithOption=money_account`, a **perps deposit** confirmation, and
**`enableMoneyAccountTransactions.perpsDeposit`**. Header title, footer
**Send** vs **Add funds**, hidden **Pay with** row, **Perps account
picker**, and the payment override in **`useDefaultPaySelectedSection`**
all read this hook instead of URL params alone.
> 
> **`useIsMoneyAccountFlagDefault`** no longer infers Money Account from
a **`default`** key or a **sibling** deposit/withdraw flag entry; the
confirmation **type must be named explicitly**, and availability uses
**`selectIsMoneyAccountTransactionEnabled`** per type.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
13f7cdb. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

### Context

Advanced gas modals read their gas limit from the active transaction.
The transaction controller now provides a selected-network node estimate
for transactions without caller-provided gas following prerequisite PR
#46548.

### Problem

When the transaction did not yet contain a gas estimate, both the
EIP-1559 and legacy advanced gas modals substituted `0x5208` (21,000).
That value could be displayed and saved while estimation was loading,
had failed, or belonged to an earlier transaction or network state.

### Solution

- Initialize both advanced gas modals from the current transaction gas
value without fabricating a fallback.
- Track the transaction shape and network client that produced the
estimate.
- Clear stale values and disable gas-limit editing and Save while an
estimate is pending or stale.
- Preserve manual gas-limit recovery when estimation fails: the input
becomes editable, explains the failure, and enables Save after the user
supplies a valid value.
- Preserve in-progress custom fee edits when the transaction controller
refreshes suggested fees.
- Link unavailable-status text to the gas input and disabled Save
action, and use numeric input mode without number-input behavior.
- Keep each advanced modal's markup local; broader modal deduplication
is deferred to a separate change.

### Implementation notes

`useAdvancedGasLimit` excludes fee fields from its transaction-shape
key. This allows users to edit EIP-1559 or legacy fee values without
invalidating the gas-limit estimate. Changes to the transaction payload,
account, chain, or network client still invalidate an unchanged gas
value until a fresh estimate arrives.

The modal content is keyed only by that transaction shape, so 10-second
gas-fee polling updates do not remount the editor and discard unsaved
custom values.

Related work:

- #46548: wallet and transaction-controller dependency alignment
- CONF-1988: native-send max gas estimation
- CONF-1989: transaction-aware gas-limit validation
- CONF-2003: cross-client integration coverage

## **Changelog**

CHANGELOG entry: Fixed advanced gas dialogs showing an incorrect default
gas limit when estimation was unavailable.

## **Related issues**

Fixes: CONF-2001

## **Manual testing steps**

1. Start a native-token send on an EIP-1559 network where the selected
RPC returns a gas estimate above 21,000.
2. Open the advanced gas modal and verify its gas-limit field displays
the transaction estimate rather than 21,000.
3. Edit the EIP-1559 fee values and gas limit, save them, reopen the
modal, and verify the custom values are preserved.
4. Repeat the flow on a legacy-fee network and verify the estimated gas
limit and custom gas price can be saved.
5. Delay `eth_estimateGas`, start another transaction, and open the
advanced gas modal while estimation is pending.
6. Verify the gas-limit field is empty and disabled, the
unavailable-status copy is visible, and Save is disabled.
7. Make `eth_estimateGas` fail and verify the gas-limit field becomes
editable with the estimation-failure explanation while Save remains
disabled.
8. Enter a valid manual gas limit, save, and verify the custom
transaction settings are applied.
9. While editing a fee value, leave the modal open for longer than the
gas-fee polling interval and verify the unsaved value is not reset.
10. Inspect the gas-limit input and verify it remains `type="text"` with
`inputmode="numeric"`.
11. While the modal is open, change the transaction data or selected
RPC/network client and verify the previous gas limit is cleared until
the updated transaction receives a fresh estimate.

## **Screenshots/Recordings**

Screenshots were not captured. The change is visible in transient
pending and failed estimation states.

### **Before**

The modal displayed 21,000 when the transaction gas estimate was
unavailable and could reset in-progress fee edits during polling.

### **After**

The modal distinguishes pending estimation from failed estimation,
explains unavailable editing, supports manual recovery after failure,
and preserves in-progress fee edits.

## **Validation**

- `yarn lint:changed:fix`
- `yarn lint:tsc`
- `yarn lint:json`
- `yarn circular-deps:check`
- 43 targeted Jest tests across four suites covering advanced gas state,
both modal variants, and the gas input

## **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 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**
> Changes how gas limits are chosen and persisted in advanced
confirmation modals, including stale-estimate invalidation and manual
recovery after failed estimation.
> 
> **Overview**
> Advanced gas modals no longer default the gas limit to **21,000** when
the transaction has no estimate. They show an empty, disabled field with
new copy (`gasLimitEditingUnavailable`) until MetaMask supplies a fresh
limit, and **Save** stays off until a valid limit exists.
> 
> A new **`useAdvancedGasLimit`** hook ties the limit to transaction
shape and network client (fee fields excluded so polling fee updates do
not wipe in-progress edits). Stale limits clear after payload, RPC, or
network changes until a new estimate arrives; after
**`eth_estimateGas`** fails, the field becomes editable with the
existing failure message so users can enter a manual limit and save.
> 
> **`GasInput`** is now controlled via props instead of reading the
confirmation context. EIP-1559 and legacy advanced modals share a new
**`AdvancedGasFeeModal`** shell for gas limit UI, disabled states, and
accessibility (`inputmode="numeric"`, help text linked to Save when
unavailable).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cd48f1d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…Failed to fetch account' (#46704)

<!--
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 console error also appears in the Edit Accounts test,

WE have a bug ticket opened here:
#46623

Temporarily ignore the error like in this other PR
#46624
#46670




## **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="997" height="91" alt="image"
src="https://github.com/user-attachments/assets/69579bf3-1a62-4ae8-b6a3-8234ce1a8334"
/>


## **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 fixture option change with no production or security impact.
> 
> **Overview**
> Adds **`ignoredConsoleErrors`** for `[PerpsStreamManager] Failed to
fetch account` on the e2e case that locks the wallet while connected to
a dapp, with a comment pointing at issue **#46623**.
> 
> This matches the temporary workaround already used in other specs
(e.g. edit-account permissions) so the test does not fail on a known,
unrelated console error until the underlying bug is fixed.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
efb4c27. 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**

<!--
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?
-->
The Bridge API now returns the native minimum balance that must stay in
the source account on quote.feeData.reserve, e.g. the Stellar reserve
for a new trustline (

[va-mmcx-bridge-api#1327](consensys-vertical-apps/va-mmcx-bridge-api#1327)).
@metamask/bridge-controller exposes it on V2 quotes (
[MetaMask/core#10241](MetaMask/core#10241)).
This PR uses it to warn users before a trade fails.

Bump @metamask/bridge-controller to ^81.3.0 (deduped with
bridge-status-controller).
Native source (XLM): the "use max" reserve banner uses the quote
reserve, with a fallback to the existing per-chain minimum.
The reserve is never counted as a fee or quote cost.

## **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: Added a warning in Swap and Bridge when a Stellar quote
requires keeping a minimum XLM balance that the account cannot cover

## **Related issues**

Fixes:

## **Manual testing steps**

1. XLM source: enter an amount that leaves less than the quote reserve.
The reserve banner shows, and "use max" sets balance - fee - reserve.
2. USDC source, not enough XLM: use an account with less than fee +
reserve in XLM (e.g. 0.4 XLM for a new trustline). The "buy more XLM"
error shows.
3. USDC source, enough XLM: repeat step 2 with enough XLM. No warning
shows.
4. Bitcoin: check that the existing chain-minimum banner still works.

## **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**

- [w] 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**
> Changes pre-submit bridge/swap validation and max-amount math;
incorrect reserve handling could block valid trades or miss failed ones,
though behavior is covered by new tests and stays UI-side.
> 
> **Overview**
> Bumps **`@metamask/bridge-controller`** to **^81.3.0** so the UI can
read **`quote.feeData.reserve`** (e.g. Stellar minimum XLM for
trustlines) from bridge quotes.
> 
> Bridge validation now **prefers the quote-carried reserve** over
static per-chain minimums when computing the **“use max” / insufficient
native reserve** banner (`getNativeReserve`, updated
**`getActiveQuoteInsufficientNativeReserveError`**). That path applies
when the quote includes a reserve, not only on Bitcoin.
> 
> **Gas sufficiency** (`hasSufficientGasForQuote`, including analytics)
uses new **`resolveGasCheckMinimumBalance`**: for a **native** source,
the quote reserve stays in the reserve banner only (not added to the gas
minimum); for a **token** source, native balance must cover **fees plus
the quote reserve**, surfacing the “buy more native” error when XLM (or
similar) is too low.
> 
> Tests cover quote override on BTC, Stellar native vs USDC sources, and
metrics gas checks.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a07c200. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…0 cp-13-52-0 (#47054)

- fix: money transaction ids default to 0x0 cp-13-52-0 (#47035)

<!--
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**
Deposits paid through MM pay's relay strategy were showing a placeholder
hash in the money account details screen of `0x0`. This is due to a
placeholder hash being persisted by the transaction controller.

This PR adds a helper function called getMoneyActivityTransactionHash
which tries to find a legit hash to display, in the case that we see
this palceholder hash. It looks for:
  1. the transaction's own hash, if it's a valid 32-byte hash;
  2. otherwise, the hash of the last required transaction Pay submitted
     for the deposit (the source leg that actually moved the funds);
  3. otherwise, metamaskPay.sourceHash, on the payment token's chain.

In the longer term we should fix this in the core package. 


<!--
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: don't display 0x0 placeholder hash in money account
details screens

## **Related issues**

Fixes: N/A

## **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]
> **Low Risk**
> Display-only change on the Money transaction details screen with
fallback logic covered by tests; no changes to signing, submission, or
payment execution.
> 
> **Overview**
> Fixes Money transaction details showing the **`0x0`** placeholder for
MetaMask Pay relay deposits by resolving a real on-chain hash before
display, copy, and block explorer links.
> 
> Adds **`getMoneyActivityTransactionHash`**, which prefers the
transaction’s own valid hash, then the last **required** Pay source
transaction, then **`metamaskPay.sourceHash`** (with the payment chain).
The details page loads the required transaction from the store and
**omits** the transaction ID row when nothing valid is available. Unit
and page tests cover the placeholder and fallback paths.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4088f01. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
[44b714f](44b714f)

Co-authored-by: John Whiles <john.whiles@consensys.net>
@metamask-ci

metamask-ci Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor
Builds ready [9343a9f]
⚡ Performance Benchmarks (Total: 🟢 10 pass · 🟡 0 warn · 🔴 3 fail)

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

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

Regressions (🔴 3 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: +414%
  • ↑ loadNewAccount/total: +414%
  • ↓ loadNewAccount/fcp: -18%
  • ↓ loadNewAccount/lcp: -12%
  • ↑ confirmTx/longTaskCount: +33%
  • ↑ confirmTx/longTaskTotalDuration: +33%
  • ↑ confirmTx/longTaskMaxDuration: +39%
  • ↑ confirmTx/tbt: +43%
  • ↑ confirmTx/inp: +14%
  • ↓ confirmTx/fcp: -16%
  • ↓ confirmTx/lcp: -13%
  • ↑ bridgeUserActions/bridge_load_page: +25%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +41%
  • ↓ bridgeUserActions/bridge_search_token: -10%
  • ↑ bridgeUserActions/longTaskCount: +11%
  • ↓ bridgeUserActions/longTaskMaxDuration: -19%
  • ↓ bridgeUserActions/tbt: -34%
  • ↓ bridgeUserActions/fcp: -16%
  • ↓ bridgeUserActions/lcp: -10%
  • ↑ loadNewAccount/load_new_account: +419%
  • ↑ loadNewAccount/total: +419%
  • ↓ loadNewAccount/fcp: -49%
  • ↑ loadNewAccount/lcp: +1018%
  • ↓ confirmTx/longTaskCount: -100%
  • ↓ confirmTx/longTaskTotalDuration: -100%
  • ↓ confirmTx/longTaskMaxDuration: -100%
  • ↓ confirmTx/tbt: -100%
  • ↓ confirmTx/inp: -24%
  • ↓ confirmTx/fcp: -54%
  • ↑ confirmTx/lcp: +1079%
  • ↑ bridgeUserActions/bridge_load_page: +265%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +176%
  • ↓ bridgeUserActions/longTaskCount: -100%
  • ↓ bridgeUserActions/longTaskTotalDuration: -100%
  • ↓ bridgeUserActions/longTaskMaxDuration: -100%
  • ↓ bridgeUserActions/tbt: -100%
  • ↑ bridgeUserActions/total: +39%
  • ↑ bridgeUserActions/inp: +23%
  • ↓ bridgeUserActions/fcp: -46%
  • ↑ bridgeUserActions/lcp: +1062%
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/firstReactRender: -99%
  • ↓ startupStandardHome/initialActions: -33%
  • ↑ startupStandardHome/setupStore: +74%
  • ↑ startupStandardHome/numNetworkReqs: +10%
  • ↓ startupStandardHome/domInteractive: -39%
  • ↓ startupStandardHome/firstReactRender: -98%
  • ↓ startupStandardHome/initialActions: -50%
  • ↑ startupStandardHome/setupStore: +163%
  • ↓ startupStandardHome/fcp: -34%
User Journey Benchmarks · Samples: 5 · real API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 total

📈 Results compared to the previous 5 runs on main

  • ↑ onboardingImportWallet/metricsToWalletReadyScreen: +68%
  • ↓ onboardingImportWallet/doneButtonToHomeScreen: -95%
  • ↓ onboardingImportWallet/openAccountMenuToAccountListLoaded: -75%
  • ↓ onboardingImportWallet/longTaskCount: -100%
  • ↓ onboardingImportWallet/longTaskTotalDuration: -100%
  • ↓ onboardingImportWallet/longTaskMaxDuration: -100%
  • ↓ onboardingImportWallet/tbt: -100%
  • ↓ onboardingImportWallet/total: -89%
  • ↑ onboardingNewWallet/srpButtonToPwForm: +29%
  • ↑ onboardingNewWallet/createPwToRecoveryScreen: +11%
  • ↑ onboardingNewWallet/skipBackupToMetricsScreen: +29%
  • ↓ onboardingNewWallet/doneButtonToAssetList: -83%
  • ↓ onboardingNewWallet/longTaskCount: -69%
  • ↓ onboardingNewWallet/longTaskTotalDuration: -79%
  • ↓ onboardingNewWallet/longTaskMaxDuration: -50%
  • ↓ onboardingNewWallet/tbt: -98%
  • ↓ onboardingNewWallet/total: -80%
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.6 MiB +39.08 KiB +0.25%
✅ ui 19 MiB -15.46 KiB -0.08%
✅ common 0 Bytes 0 Bytes 0.00%
✅ other 1.69 MiB 0 Bytes 0.00%
🚨 content scripts 1.98 MiB +4.74 KiB +0.23%
🚨 zip 22.86 MiB +61.63 KiB +0.26%
### 🍒 What's in this RC

Cherry-picks (10 commits)
Commit Description
9343a9fad5 release(runway): cherry-pick fix: money transaction ids default to 0x0 cp-13-52-0 (#47054)
6d25ea9ad3 chore: update CHANGELOG.md
466655ba00 Merge pull request #47030 from MetaMask/runway-cherry-pick-13.52.0-1791827722
fd8c96dc90 release(runway): cherry-pick chore: ignore disputed braces advisory cp-13.52.0 (#47029)
80362e1cc8 release(runway): cherry-pick fix(confirmations): block Money deposits while the Max quote is stale (#47028)
8349bf2f67 release(runway): cherry-pick chore: audit braces and http-cache-semantics (#47020)
43a929f156 release(runway): cherry-pick feat(perps): update controller to 20.0.0 (#46999)
ef1992e50f Merge pull request #46976 from MetaMask/stable-sync-release-13.52.0
c4d177b1a0 release: dedupe, update LavaMoat policies, update attributions
79f124fd19 Merge branch 'release/13.52.0' into stable-sync-release-13.52.0

Changelog (113 commits since v13.51.0)
Commit Description
9343a9fad5 release(runway): cherry-pick fix: money transaction ids default to 0x0 cp-13-52-0 (#47054)
6d25ea9ad3 chore: update CHANGELOG.md
fd8c96dc90 release(runway): cherry-pick chore: ignore disputed braces advisory cp-13.52.0 (#47029)
80362e1cc8 release(runway): cherry-pick fix(confirmations): block Money deposits while the Max quote is stale (#47028)
8349bf2f67 release(runway): cherry-pick chore: audit braces and http-cache-semantics (#47020)
43a929f156 release(runway): cherry-pick feat(perps): update controller to 20.0.0 (#46999)
c4d177b1a0 release: dedupe, update LavaMoat policies, update attributions
89f5b8d730 release: release-changelog/13.52.0 (#46849)
582c7ea8e8 release(runway): cherry-pick bump(stellar): bump stellar-wallet-snap from 1.1.0 to 1.1.1 (#46974)
94dfd48540 release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46946)
1a91331f8a release(runway): cherry-pick fix: show Arc USDC balance and details from native asset (#46933)
190e415a13 release(runway): cherry-pick fix(metrics): report money account type on deposit batches 13.52.0 (#46940)
bf51470750 release(runway): cherry-pick fix: stellar missing from send loader (#46930)
ebd93b0658 test(assets): update unified asset UI coverage (#46729)
25ac31b35b fix: prefer metamask.com install attribution cookies (#46801)
b5b3636121 ci: limit AI PR risk analysis to auto-merge and release PRs (#46745)
70608a756b feat(confirmations): hide no-fee tags for hardware payers (#46366)
689e2d0b90 chore: update trust signal badges UI on swap assets (#46813)
06998a4124 feat: allow redirect after unlock (#46821)
c240f521df feat: add trust signals to send picker (#46806)
60b14cdd72 fix: revert show failed toast for failed approved or signed transactions (#46814)
aaa203acc8 test: fix flaky test Test Snap manageState can use the new state API (#46836)
91fac2468e feat(confirmations): use paying account for MM Pay gas balance (#46365)
5670d0c0d5 test: fix flaky test Forgot password resets password and sets up biometrics with passkey (#46833)
e047541013 refactor(assets): use unified asset selectors in the UI (#46728)
dfd994472d refactor(networks): migrate the add network form to the design system (#46792)
31717f6308 fix(perps): make loading skeleton buttons pill-shaped (#46826)
cd52c6ef33 chore: upgrade @metamask/stellar-wallet-snap to 1.1.0 (#46681)
3c658fb1de fix(accounts): vertically align row arrows on account details page (#46822)
c6f3fb40d1 fix(accounts): remove redundant rename back button (#46820)
aa3e307d30 chore: New Crowdin Translations by GitHub Action cp-13.51.0 (#46639)
4b2196cb66 chore: audit webpack-dev-middleware and undici for security advisories (#46802)
4d74691b20 chore(ramps): send extension payment context (#46714)
77c31a48ef refactor(analytics): remove MetaMetricsController (replaced by AnalyticsController) (#46690)
319ff58119 fix: inject popup opener action dependency (#46757)
1f017de6f2 refactor(money): remove raw balance scrubbing from client (#46815)
2645a16553 refactor(7407): migrate development build tooling batch 1 to TypeScript (#46675)
ff80ec2fdd feat: add volume/market cap and fully diluted rows to market details (#46788)
cc6eac16be chore: harden the platform API docs deploy workflow (#46811)
530d70c3cf fix(sentry): use globalThis to share remote rates across MV2 bundle chunks (#46791)
3ecce56933 feat(money): update api balance source fetch on transaction confirmed (#46785)
8a54a34b9c feat(ramps): use synced orders in the buy flow (#46046)
05d3ef4dc6 feat: generate and publish Platform API docs (#40352)
679bc4e66c refactor(networks): migrate network form controls to design system (#46591)
2e2feb7059 fix: extension: No position found for [market ticker] (#46606)
d0f28f6ef8 fix(perps): add missing perp events/parameters on extension (#46607)
838ab71730 feat(metamask-pay): read asset state directly from AssetsController (#46706)
71e7755f2d chore: replaced robinhood explorer with new one from etherscan (#46742)
41210733a9 feat(confirmations): Ledger funding for Money Account deposits (#46364)
9f5152bf58 chore(assets): remove unified state flag configuration (#46727)
c149c050e8 feat: add trust signals to Manage tokens screen (#46789)
368dbc041a test: Unskip 2 Asset tests (#46799)
5e715a210b fix(tokens): use standard hide token modal (#46797)
393289317b fix: use MMDS modal and buttons in marketing consent modal (#46758)
f80c06a1e6 fix(ramps): hide check icon when quote info is displayed (#46717)
148e4b72e9 test: Sync Feature Flag Registry - 2026-09-29 01:22 UTC (#46762)
7057ceb713 ci: use artifacts instead of cache for update-attributions workflow (#46775)
550927a614 feat: support metamask.com deep links (#46530)
7c1520a5b5 fix: test network visibility and the “Show test networks” toggle when dapps use testnets (#46716)
d4b1509253 test: adapt E2E for consolidated basic functionality (#46769)
1e65e76a24 test: adapt perps withdraw E2E to mUSD pay token (#46770)
b848cb72ae feat(money): hide hardware accounts from selectors (#46778)
28b8fde1ca refactor(assets): remove legacy asset polling hooks (#46726)
cf27ef1669 chore(migrations): convert migrations 054-077, 079-080, and 106 to TypeScript (#46673)
c3cca25933 feat(send): instrument non-EVM send failures (#46780)
96ca52e874 chore(assets-controller): bump to 17.0.0 (#46705)
c6a2845fa1 refactor(assets): route controller wiring through unified state (#46725)
e679236b61 refactor(7382): migrate background entrypoint to TypeScript (#46625)
f6b0c26181 feat: add trust signal badges to rwas (#46772)
78be9d8550 release: Bump main version to 13.52.0 (#46779)
8960877706 fix(ramps): update payment method limit and delay copy to match designs (#46718)
d7c39acdba refactor(assets): make background asset state always unified (#46314)
ad4cd985b2 fix: metamask shield metrics event (#46698)
2d7f05cd25 chore: upgrade @metamask/transaction-controller to 72.0.1 (#46676)
dd237c40c8 fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components cp-13.51.0 (#46700)
bf87116170 build: bump webpack to 5.111.1 and terser-webpack-plugin to 5.6.1 (#46562)
f403d045a3 refactor: migrate helptext to use new MMDS (#46720)
95fdb2d0db feat: updated single network label in network filters (#46702)
1b8790a772 chore: fix yarn lint OOM (#46750)
3401275dfa ci: replace LAVAMOAT_UPDATE_TOKEN with OIDC tokens (#46707)
9362f38302 feat: ensure parity with mobile money account events tracking cp-13.51.0 (#46724)
9b0100ccd3 test: fix flaky test Send ERC20 dApp initiated sends ERC20 token (#46710)
3efcb62165 test: fix flaky test Tron account derivation Shows each account Tron address on the quick-copy popup and copies it (#46713)
3b4d338009 fix(money): remove Paid by MetaMask from transaction fee tooltips cp-13.51.0 (#46644)
a7977f6410 feat: consume reserve field from quote to display reserve banner (#46554)
c20940837a test: ignore console error in Dapp interactions [PerpsStreamManager] Failed to fetch account' (#46704)
ed39790f44 fix(confirmations): remove advanced gas limit fallback (#46464)
80ad313d5f fix(confirmations): gate money account perps pay on its feature flag cp-13.51.0 (#46668)
21c50a6e8d fix: social login init failure (#46630)
bb5a8a0f0d Merge origin/main into stable-main-13.50.0
ef46f8a695 fix: enable immer's patch plugin in the UI store (#46594)
fe44281981 feat(ramps): replace token list show-all button with scroll pagination (#46685)
4bcb9625a5 fix(ramps): display branded mUSD symbol across buy flow (#46657)
0592f3e63b fix(bft): combine social migration modal body into one paragraph cp-13.50.0 (#46641)
fb4de798d2 test: ignore console error in Edit Accounts [PerpsStreamManager] Failed to fetch account' (#46670)
d13b0e25bd fix: keep back arrow navigation inside wallet when pages are opened directly by URL (#46547)
5b1c64f30f chore: bump accounts packages (#46659)
b34a9c1725 refactor(smart-transactions): remove dead cancellation variant (#46431)
5075778fa4 fix(sentry): pin SwapQuoteFetch to its own sample rate (#46618)
d8501d4c7d feat(persistence): measure write sizes with storage bytes (#46454)
a9947270da feat(hardware-wallets): implement fast-fail for Ledger device connection and improve error handling (#46340)
43c187b336 test(e2e/tron): add activity cluster tests (#46118)
944db50835 refactor(7370): convert migrations 002–026 and loader to TypeScript (#46592)
fea22776d2 fix: Revert "fix(content): remove "please" from extension locale strings (… (#46663)
d876def0d3 fix: Revert "fix(content): remove "please" from extension locale strings (… (#46660)
fa113a03c9 fix: Revert "fix(content): remove "successfully" from extension locale str… (#46645)
ee33105db5 fix(content): remove "successfully" from extension locale strings (#46151)
54bf45557f fix(content): remove "please" from extension locale strings (batch 3 of 3) (#46150)
dab5f03c23 fix(content): remove "please" from extension locale strings (batch 2 of 3) (#46149)
e5acf447a7 fix: update Basic Functionality privacy messaging cp-13.51.0 (#46633)
ce5346e20e fix: Fix migration crash (#46632)
30f8caf231 feat(analytics): move marketingCampaignCookieId to AnalyticsController (#46600)
fac5f5bca8 chore: Update OP Mainnet network icon (#46520)

AI Test Plan

Risk Score High Risk Medium Risk Files Changed Commits
64/100 8 9 712 118
Cherry-Pick Scenarios (5)

High Risk Scenarios (3)

1. Deposit Confirmations (stale Max quote block)

Risk Level: HIGH

Why This Matters: Cherry-pick 47028 blocks deposits on stale quotes; without this, users could confirm with outdated pricing, causing incorrect deposit amounts.

Test Steps:

  1. Start a Money deposit flow and wait until the Max quote becomes stale (or simulate by delaying beyond quote validity).
  2. Attempt to confirm the deposit while the quote is stale and verify the UI blocks confirmation with a clear message.
  3. Refresh to obtain a new quote and then confirm; verify the transaction proceeds and status updates in Activity.

2. Money deposit transaction IDs (0x0 default regression)

Risk Level: HIGH

Why This Matters: Cherry-pick 47054 fixes transaction IDs defaulting to 0x0; incorrect IDs break traceability, user trust, and dapp deep links.

Test Steps:

  1. Initiate a Money deposit and proceed to confirmation and broadcast.
  2. In Activity and transaction details, verify the transaction hash is non-zero and correctly populated.
  3. For a failed or canceled attempt, verify no lingering entries display txId 0x0; retried submissions show a proper hash once broadcast.

3. Perps Controller integration (controller v20.0.0)

Risk Level: HIGH

Why This Matters: Cherry-pick 46999 updates the perps controller; compatibility issues can break trading flows or misreport positions.

Test Steps:

  1. Connect to a supported perps dapp and initiate a basic action (e.g., open a small position) that triggers controller calls.
  2. Review the confirmation screens and approve; ensure state updates reflect the new position.
  3. Close or adjust the position; verify balances and position details update without errors and no stale approvals remain.

Medium Risk Scenarios (2)

1. Send Flow Loader on Stellar

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46930 fixes Stellar missing from the send loader; without it, users may be unable to initiate Stellar sends or see broken UI.

Test Steps:

  1. Switch to the Stellar account/network supported by the Snap and open the Send flow.
  2. Verify the send loader initializes correctly and Stellar-specific UI elements load (address/memo fields, fees).
  3. Complete a small test send and verify status and activity entries are correct.

2. Arc USDC balance and details (native asset mapping)

Risk Level: MEDIUM

Why This Matters: Cherry-pick 46933 shows Arc USDC from the native asset; incorrect mapping misreports balances and can confuse users when sending or reviewing activity.

Test Steps:

  1. On the Arc network with a native Arc USDC balance, open the Home assets list.
  2. Verify the Arc USDC balance appears once (no duplication with wrapped variants) and shows correct fiat value.
  3. Open asset details for Arc USDC and verify contract/address info, recent activity, and send/receive actions are correct.

Release Scenarios (12)

High Risk Scenarios (5)

1. State Migrations (upgrade path, complex wallet state)

Risk Level: HIGH

Why This Matters: Multiple migration files changed and new migrations (including fail-tx) were added; broken migrations can cause data loss, corrupted activity, or unusable wallets.

Test Steps:

  1. Start on 13.51.x with: 2 HD accounts + 1 imported account, 1 custom network, 2 custom tokens (one duplicate), 1 installed Snap, and at least 1 connected site.
  2. Create 1 pending transaction on Ethereum (e.g., set very low gas, do not speed up) and 1 failed transaction on a test network.
  3. Update the extension to 13.52.0 and unlock the wallet; allow the migration modal to complete.
  4. Verify accounts, custom network, connected sites, and Snap remain; custom tokens are present without duplication; NFTs/collectibles still show.
  5. Verify Activity: the formerly failed tx is correctly marked failed; the pending tx status is consistent and not lost.

2. Migration resilience (interruption and recovery)

Risk Level: HIGH

Why This Matters: Interrupted migrations can leave state partially transformed; the new migration utils and modal must safely resume to prevent bricking wallets.

Test Steps:

  1. Start upgrade to 13.52.0 with a wallet that has multiple accounts and custom assets.
  2. Force-close the extension or browser while the migration modal is in progress.
  3. Reopen the extension; use the migration modal to resume or retry.
  4. Confirm the wallet opens normally with consistent balances, connected sites, and no duplicated tokens.
  5. Review console logs for migration errors and ensure no repeated migration loops.

3. Privacy & Analytics (MetaMetrics consent and gating)

Risk Level: HIGH

Why This Matters: Analytics/Metametrics controllers and consent UI changed; incorrect gating could leak user telemetry without consent.

Test Steps:

  1. Fresh install 13.52.0, go through onboarding, explicitly choose to NOT opt in to MetaMetrics.
  2. Perform key actions: add account, switch networks, add a custom token, and open Send; observe network traffic.
  3. Verify no metrics/analytics calls are sent while opted-out.
  4. In Settings > Security & Privacy > MetaMetrics, opt in; repeat actions and verify events are now sent with correct consent state.
  5. Lock/unlock and restart the browser to verify consent persists and event gating remains correct.

4. Snaps Permissions (install prompts and restricted methods)

Risk Level: HIGH

Why This Matters: Permission specifications changed; incorrect prompts or enforcement can expose unintended capabilities or block legitimate Snap functionality.

Test Steps:

  1. Install a known Snap (e.g., a sample or Stellar Snap) that requests multiple permissions.
  2. Review the permission prompt text and scope; deny once, then retry and grant.
  3. From the connected Snap, attempt a method outside granted permissions and verify it’s blocked with a clear error.
  4. Disconnect and reconnect the Snap; verify permission prompts are consistent and previously granted scopes persist.
  5. Confirm the Snap can perform only the allowed actions after reconnection.

5. Money/Fiat Onramp deposit confirmation (baseline flow)

Risk Level: HIGH

Why This Matters: Confirmations and controller logic around deposit flows are sensitive; regressions can cause incorrect deposits or misleading UI states.

Test Steps:

  1. Open the Earn/Buy/Deposit (Money) flow and obtain a quote for a deposit to a supported network/account.
  2. Proceed through confirmation screens and review all calculated values (amount, fees, network).
  3. Confirm the deposit and track status through the activity feed until completion or failure.
  4. Cancel or back out before confirmation; verify no orphaned activity entries are created.
  5. Re-open the flow, change input amounts/currency, and ensure recalculated totals update correctly.

Medium Risk Scenarios (7)

1. Asset List Network Filter and Labeling

Risk Level: MEDIUM

Why This Matters: Asset list control bar and filter label logic changed; wrong filters or labels mislead users about where funds are.

Test Steps:

  1. On the Home screen, open the asset list filter and restrict to a single network.
  2. Verify the filter button label reflects the chosen network and updates when switching networks.
  3. Reload the extension; confirm the filter state and label persist.
  4. Clear the filter (All networks) and verify totals and per-asset balances adjust accordingly.

2. Account Group Balance accuracy with filters

Risk Level: MEDIUM

Why This Matters: Account group balance and filtering logic changed; inaccurate totals can cause user confusion and poor decisions.

Test Steps:

  1. With multiple accounts holding assets on different networks, view the account group balance.
  2. Apply a single-network filter and manually verify the sum matches the displayed group total.
  3. Switch to All networks and confirm the total updates and includes cross-network holdings.
  4. Toggle hide small balances (if available) and confirm totals adjust correctly.

3. Static Assets (token/network logos and fallbacks)

Risk Level: MEDIUM

Why This Matters: Static assets controller changes can break icons; visual regressions erode trust and hinder quick identification of assets.

Test Steps:

  1. Add a well-known token with a logo and verify the icon appears on Home and asset details.
  2. Add a custom token without a known logo and verify a placeholder appears consistently.
  3. Simulate offline mode and reload; verify cached icons still display or graceful fallbacks are used.
  4. Remove the tokens and confirm no broken images persist in activity or history.

4. Asset Migration (custom/watched tokens and deduplication)

Risk Level: MEDIUM

Why This Matters: Shared asset migration selectors changed; incorrect transforms can duplicate, hide, or drop user tokens.

Test Steps:

  1. Before upgrading, add the same custom token via different methods (watch + import) to simulate duplication.
  2. Upgrade to 13.52.0; unlock and allow migrations to finish.
  3. Verify the token list is deduplicated and balances remain accurate.
  4. Confirm hiding/unhiding tokens still works and persists across reload.

5. MetaMetrics settings page behavior

Risk Level: MEDIUM

Why This Matters: Consent UI component changed; mis-synced UI and controller states can cause unintended data collection or missed telemetry.

Test Steps:

  1. Navigate to Settings > Security & Privacy > MetaMetrics.
  2. Toggle analytics consent off and on; confirm real-time persistence without needing a restart.
  3. Perform an action (e.g., send, switch network) after each toggle and verify event behavior matches the current setting.
  4. Lock/unlock the wallet and confirm the chosen setting is retained.

6. Connected sites and approvals post-migration

Risk Level: MEDIUM

Why This Matters: Broad controller and migration changes can break site connections or approvals, disrupting dapp usage.

Test Steps:

  1. Prior to upgrade, connect to 1–2 dapps across different networks and approve token allowances.
  2. Upgrade to 13.52.0 and unlock after migrations finish.
  3. Visit the same dapps; verify connections persist and previously approved allowances remain usable.
  4. Revoke a connection and reconnect to confirm flows still work.

7. Snaps lifecycle (enable/disable/uninstall) after migrations

Risk Level: MEDIUM

Why This Matters: Permission spec and controller changes can leave stale Snap states or permissions, reducing security and stability.

Test Steps:

  1. With a Snap installed pre-upgrade, update to 13.52.0 and unlock.
  2. Disable and re-enable the Snap from Settings.
  3. Uninstall and reinstall the Snap; verify prompts and capabilities behave consistently.
  4. Confirm no orphaned permissions remain after uninstall.

Teams Sign-off Status

Signed off: None yet

Awaiting sign-off (10):
Accounts, Assets, Confirmations, Networks, Permissions, Security, Settings, Snaps, Transactions, Wallet


Generated by AI Test Plan Analyzer (gpt-5) at 2026-10-13T14:33:43.992Z

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

@metamask-ci

metamask-ci Bot commented Oct 13, 2026

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

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 38440079642 | 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/longTaskCount: +33%
  • ↑ confirmTx/longTaskTotalDuration: +68%
  • ↑ confirmTx/longTaskMaxDuration: +72%
  • ↑ confirmTx/tbt: +92%
  • ↑ bridgeUserActions/bridge_load_page: +54%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +78%
  • ↑ bridgeUserActions/longTaskCount: +67%
  • ↑ bridgeUserActions/longTaskTotalDuration: +101%
  • ↑ bridgeUserActions/longTaskMaxDuration: +34%
  • ↑ bridgeUserActions/tbt: +177%
  • ↑ loadNewAccount/load_new_account: +414%
  • ↑ loadNewAccount/total: +414%
  • ↓ loadNewAccount/inp: -47%
  • ↓ loadNewAccount/fcp: -78%
  • ↑ loadNewAccount/lcp: +920%
  • ↓ confirmTx/longTaskCount: -100%
  • ↓ confirmTx/longTaskTotalDuration: -100%
  • ↓ confirmTx/longTaskMaxDuration: -100%
  • ↓ confirmTx/tbt: -100%
  • ↓ confirmTx/inp: -39%
  • ↓ confirmTx/fcp: -82%
  • ↑ confirmTx/lcp: +939%
  • ↑ bridgeUserActions/bridge_load_page: +113%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +53%
  • ↓ bridgeUserActions/bridge_search_token: -14%
  • ↓ bridgeUserActions/longTaskCount: -100%
  • ↓ bridgeUserActions/longTaskTotalDuration: -100%
  • ↓ bridgeUserActions/longTaskMaxDuration: -100%
  • ↓ bridgeUserActions/tbt: -100%
  • ↓ bridgeUserActions/fcp: -80%
  • ↑ bridgeUserActions/lcp: +741%

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

  • 🟡 loadNewAccount/FCP: p75 1.9s
  • 🟡 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/firstReactRender: -99%
  • ↓ startupStandardHome/initialActions: -33%
  • ↑ startupStandardHome/setupStore: +97%
  • ↑ startupStandardHome/numNetworkReqs: +10%
  • ↓ startupStandardHome/longTaskCount: -33%
  • ↓ startupStandardHome/domInteractive: -29%
  • ↓ startupStandardHome/firstReactRender: -98%
  • ↓ startupStandardHome/initialActions: -50%
  • ↑ startupStandardHome/setupStore: +163%
  • ↓ startupStandardHome/fcp: -27%
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: +40%
  • ↑ onboardingImportWallet/confirmSrpToPwForm: +49%
  • ↑ onboardingImportWallet/pwFormToMetricsScreen: +28%
  • ↑ onboardingImportWallet/metricsToWalletReadyScreen: +78%
  • ↓ onboardingImportWallet/doneButtonToHomeScreen: -94%
  • ↓ onboardingImportWallet/openAccountMenuToAccountListLoaded: -75%
  • ↓ onboardingImportWallet/longTaskCount: -100%
  • ↓ onboardingImportWallet/longTaskTotalDuration: -100%
  • ↓ onboardingImportWallet/longTaskMaxDuration: -100%
  • ↓ onboardingImportWallet/tbt: -100%
  • ↓ onboardingImportWallet/total: -89%
  • ↑ onboardingNewWallet/srpButtonToPwForm: +30%
  • ↑ onboardingNewWallet/createPwToRecoveryScreen: +26%
  • ↑ onboardingNewWallet/skipBackupToMetricsScreen: +31%
  • ↑ onboardingNewWallet/agreeButtonToOnboardingSuccess: +42%
  • ↓ onboardingNewWallet/doneButtonToAssetList: -83%
  • ↓ onboardingNewWallet/longTaskCount: -69%
  • ↓ onboardingNewWallet/longTaskTotalDuration: -77%
  • ↓ onboardingNewWallet/longTaskMaxDuration: -46%
  • ↓ onboardingNewWallet/tbt: -96%
  • ↓ onboardingNewWallet/total: -79%
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: -58%
  • ↓ dappPageLoad/firstPaint: -46%
  • ↓ dappPageLoad/firstContentfulPaint: -46%
Bundle Size Diffs [🚨 Warning! Bundle size has increased!]
Status Bundle Total Diff Change
🚨 background 15.6 MiB +39.08 KiB +0.25%
✅ ui 19 MiB -15.46 KiB -0.08%
✅ common 0 Bytes 0 Bytes 0.00%
✅ other 1.69 MiB 0 Bytes 0.00%
🚨 content scripts 1.98 MiB +4.74 KiB +0.23%
🚨 zip 22.86 MiB +61.63 KiB +0.26%
### 🍒 What's in this RC

Cherry-picks (11 commits)
Commit Description
217c943356 chore: update CHANGELOG.md
9343a9fad5 release(runway): cherry-pick fix: money transaction ids default to 0x0 cp-13-52-0 (#47054)
6d25ea9ad3 chore: update CHANGELOG.md
466655ba00 Merge pull request #47030 from MetaMask/runway-cherry-pick-13.52.0-1791827722
fd8c96dc90 release(runway): cherry-pick chore: ignore disputed braces advisory cp-13.52.0 (#47029)
80362e1cc8 release(runway): cherry-pick fix(confirmations): block Money deposits while the Max quote is stale (#47028)
8349bf2f67 release(runway): cherry-pick chore: audit braces and http-cache-semantics (#47020)
43a929f156 release(runway): cherry-pick feat(perps): update controller to 20.0.0 (#46999)
ef1992e50f Merge pull request #46976 from MetaMask/stable-sync-release-13.52.0
c4d177b1a0 release: dedupe, update LavaMoat policies, update attributions
79f124fd19 Merge branch 'release/13.52.0' into stable-sync-release-13.52.0

Changelog (114 commits since v13.51.0)
Commit Description
217c943356 chore: update CHANGELOG.md
9343a9fad5 release(runway): cherry-pick fix: money transaction ids default to 0x0 cp-13-52-0 (#47054)
6d25ea9ad3 chore: update CHANGELOG.md
fd8c96dc90 release(runway): cherry-pick chore: ignore disputed braces advisory cp-13.52.0 (#47029)
80362e1cc8 release(runway): cherry-pick fix(confirmations): block Money deposits while the Max quote is stale (#47028)
8349bf2f67 release(runway): cherry-pick chore: audit braces and http-cache-semantics (#47020)
43a929f156 release(runway): cherry-pick feat(perps): update controller to 20.0.0 (#46999)
c4d177b1a0 release: dedupe, update LavaMoat policies, update attributions
89f5b8d730 release: release-changelog/13.52.0 (#46849)
582c7ea8e8 release(runway): cherry-pick bump(stellar): bump stellar-wallet-snap from 1.1.0 to 1.1.1 (#46974)
94dfd48540 release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action (#46946)
1a91331f8a release(runway): cherry-pick fix: show Arc USDC balance and details from native asset (#46933)
190e415a13 release(runway): cherry-pick fix(metrics): report money account type on deposit batches 13.52.0 (#46940)
bf51470750 release(runway): cherry-pick fix: stellar missing from send loader (#46930)
ebd93b0658 test(assets): update unified asset UI coverage (#46729)
25ac31b35b fix: prefer metamask.com install attribution cookies (#46801)
b5b3636121 ci: limit AI PR risk analysis to auto-merge and release PRs (#46745)
70608a756b feat(confirmations): hide no-fee tags for hardware payers (#46366)
689e2d0b90 chore: update trust signal badges UI on swap assets (#46813)
06998a4124 feat: allow redirect after unlock (#46821)
c240f521df feat: add trust signals to send picker (#46806)
60b14cdd72 fix: revert show failed toast for failed approved or signed transactions (#46814)
aaa203acc8 test: fix flaky test Test Snap manageState can use the new state API (#46836)
91fac2468e feat(confirmations): use paying account for MM Pay gas balance (#46365)
5670d0c0d5 test: fix flaky test Forgot password resets password and sets up biometrics with passkey (#46833)
e047541013 refactor(assets): use unified asset selectors in the UI (#46728)
dfd994472d refactor(networks): migrate the add network form to the design system (#46792)
31717f6308 fix(perps): make loading skeleton buttons pill-shaped (#46826)
cd52c6ef33 chore: upgrade @metamask/stellar-wallet-snap to 1.1.0 (#46681)
3c658fb1de fix(accounts): vertically align row arrows on account details page (#46822)
c6f3fb40d1 fix(accounts): remove redundant rename back button (#46820)
aa3e307d30 chore: New Crowdin Translations by GitHub Action cp-13.51.0 (#46639)
4b2196cb66 chore: audit webpack-dev-middleware and undici for security advisories (#46802)
4d74691b20 chore(ramps): send extension payment context (#46714)
77c31a48ef refactor(analytics): remove MetaMetricsController (replaced by AnalyticsController) (#46690)
319ff58119 fix: inject popup opener action dependency (#46757)
1f017de6f2 refactor(money): remove raw balance scrubbing from client (#46815)
2645a16553 refactor(7407): migrate development build tooling batch 1 to TypeScript (#46675)
ff80ec2fdd feat: add volume/market cap and fully diluted rows to market details (#46788)
cc6eac16be chore: harden the platform API docs deploy workflow (#46811)
530d70c3cf fix(sentry): use globalThis to share remote rates across MV2 bundle chunks (#46791)
3ecce56933 feat(money): update api balance source fetch on transaction confirmed (#46785)
8a54a34b9c feat(ramps): use synced orders in the buy flow (#46046)
05d3ef4dc6 feat: generate and publish Platform API docs (#40352)
679bc4e66c refactor(networks): migrate network form controls to design system (#46591)
2e2feb7059 fix: extension: No position found for [market ticker] (#46606)
d0f28f6ef8 fix(perps): add missing perp events/parameters on extension (#46607)
838ab71730 feat(metamask-pay): read asset state directly from AssetsController (#46706)
71e7755f2d chore: replaced robinhood explorer with new one from etherscan (#46742)
41210733a9 feat(confirmations): Ledger funding for Money Account deposits (#46364)
9f5152bf58 chore(assets): remove unified state flag configuration (#46727)
c149c050e8 feat: add trust signals to Manage tokens screen (#46789)
368dbc041a test: Unskip 2 Asset tests (#46799)
5e715a210b fix(tokens): use standard hide token modal (#46797)
393289317b fix: use MMDS modal and buttons in marketing consent modal (#46758)
f80c06a1e6 fix(ramps): hide check icon when quote info is displayed (#46717)
148e4b72e9 test: Sync Feature Flag Registry - 2026-09-29 01:22 UTC (#46762)
7057ceb713 ci: use artifacts instead of cache for update-attributions workflow (#46775)
550927a614 feat: support metamask.com deep links (#46530)
7c1520a5b5 fix: test network visibility and the “Show test networks” toggle when dapps use testnets (#46716)
d4b1509253 test: adapt E2E for consolidated basic functionality (#46769)
1e65e76a24 test: adapt perps withdraw E2E to mUSD pay token (#46770)
b848cb72ae feat(money): hide hardware accounts from selectors (#46778)
28b8fde1ca refactor(assets): remove legacy asset polling hooks (#46726)
cf27ef1669 chore(migrations): convert migrations 054-077, 079-080, and 106 to TypeScript (#46673)
c3cca25933 feat(send): instrument non-EVM send failures (#46780)
96ca52e874 chore(assets-controller): bump to 17.0.0 (#46705)
c6a2845fa1 refactor(assets): route controller wiring through unified state (#46725)
e679236b61 refactor(7382): migrate background entrypoint to TypeScript (#46625)
f6b0c26181 feat: add trust signal badges to rwas (#46772)
78be9d8550 release: Bump main version to 13.52.0 (#46779)
8960877706 fix(ramps): update payment method limit and delay copy to match designs (#46718)
d7c39acdba refactor(assets): make background asset state always unified (#46314)
ad4cd985b2 fix: metamask shield metrics event (#46698)
2d7f05cd25 chore: upgrade @metamask/transaction-controller to 72.0.1 (#46676)
dd237c40c8 fix(stellar): Prioritizes route-derived CAIP19 asset IDs in asset details page for Stellar Components cp-13.51.0 (#46700)
bf87116170 build: bump webpack to 5.111.1 and terser-webpack-plugin to 5.6.1 (#46562)
f403d045a3 refactor: migrate helptext to use new MMDS (#46720)
95fdb2d0db feat: updated single network label in network filters (#46702)
1b8790a772 chore: fix yarn lint OOM (#46750)
3401275dfa ci: replace LAVAMOAT_UPDATE_TOKEN with OIDC tokens (#46707)
9362f38302 feat: ensure parity with mobile money account events tracking cp-13.51.0 (#46724)
9b0100ccd3 test: fix flaky test Send ERC20 dApp initiated sends ERC20 token (#46710)
3efcb62165 test: fix flaky test Tron account derivation Shows each account Tron address on the quick-copy popup and copies it (#46713)
3b4d338009 fix(money): remove Paid by MetaMask from transaction fee tooltips cp-13.51.0 (#46644)
a7977f6410 feat: consume reserve field from quote to display reserve banner (#46554)
c20940837a test: ignore console error in Dapp interactions [PerpsStreamManager] Failed to fetch account' (#46704)
ed39790f44 fix(confirmations): remove advanced gas limit fallback (#46464)
80ad313d5f fix(confirmations): gate money account perps pay on its feature flag cp-13.51.0 (#46668)
21c50a6e8d fix: social login init failure (#46630)
bb5a8a0f0d Merge origin/main into stable-main-13.50.0
ef46f8a695 fix: enable immer's patch plugin in the UI store (#46594)
fe44281981 feat(ramps): replace token list show-all button with scroll pagination (#46685)
4bcb9625a5 fix(ramps): display branded mUSD symbol across buy flow (#46657)
0592f3e63b fix(bft): combine social migration modal body into one paragraph cp-13.50.0 (#46641)
fb4de798d2 test: ignore console error in Edit Accounts [PerpsStreamManager] Failed to fetch account' (#46670)
d13b0e25bd fix: keep back arrow navigation inside wallet when pages are opened directly by URL (#46547)
5b1c64f30f chore: bump accounts packages (#46659)
b34a9c1725 refactor(smart-transactions): remove dead cancellation variant (#46431)
5075778fa4 fix(sentry): pin SwapQuoteFetch to its own sample rate (#46618)
d8501d4c7d feat(persistence): measure write sizes with storage bytes (#46454)
a9947270da feat(hardware-wallets): implement fast-fail for Ledger device connection and improve error handling (#46340)
43c187b336 test(e2e/tron): add activity cluster tests (#46118)
944db50835 refactor(7370): convert migrations 002–026 and loader to TypeScript (#46592)
fea22776d2 fix: Revert "fix(content): remove "please" from extension locale strings (… (#46663)
d876def0d3 fix: Revert "fix(content): remove "please" from extension locale strings (… (#46660)
fa113a03c9 fix: Revert "fix(content): remove "successfully" from extension locale str… (#46645)
ee33105db5 fix(content): remove "successfully" from extension locale strings (#46151)
54bf45557f fix(content): remove "please" from extension locale strings (batch 3 of 3) (#46150)
dab5f03c23 fix(content): remove "please" from extension locale strings (batch 2 of 3) (#46149)
e5acf447a7 fix: update Basic Functionality privacy messaging cp-13.51.0 (#46633)
ce5346e20e fix: Fix migration crash (#46632)
30f8caf231 feat(analytics): move marketingCampaignCookieId to AnalyticsController (#46600)
fac5f5bca8 chore: Update OP Mainnet network icon (#46520)

AI Test Plan

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

High Risk Scenarios (2)

1. Perpetuals (Perps) - Trading and Collateral

Risk Level: HIGH

Why This Matters: Cherry-pick #46999 updates the perps controller to 20.0.0; controller upgrades can break order placement, margin accounting, and position persistence.

Test Steps:

  1. Open the Perps feature, connect an EVM account, and deposit collateral; confirm any required transactions.
  2. Place a market order and verify confirmations complete with accurate position and PnL updates.
  3. Close/cancel an order and ensure balances and positions reflect the change immediately.
  4. Reload the extension and confirm positions, margins, and recent activity persist and display correctly.
  5. Attempt a second trade to ensure no residual state or errors from the first session.

2. Money Deposits - Stale Quote Blocking

Risk Level: HIGH

Why This Matters: Cherry-pick #47028 fixes blocking deposits when the Max quote is stale; without this, users could submit invalid or mispriced deposits.

Test Steps:

  1. Start a Money deposit flow and allow the quote to expire (or wait until it becomes stale on the confirmation screen).
  2. Verify the Confirm/Deposit action is disabled and a clear warning indicates the quote is stale.
  3. Refresh the quote from within the flow and confirm the action becomes enabled only after a fresh quote is retrieved.
  4. Complete the deposit successfully and ensure the activity/history reflects the correct amount and state.

Medium Risk Scenarios (3)

1. Arc USDC - Asset Details and Balance Display

Risk Level: MEDIUM

Why This Matters: Cherry-pick #46933 fixes Arc USDC details sourced from the native asset; incorrect metadata leads to wrong balances, icons, and potential double counting.

Test Steps:

  1. Switch to the Arc network and ensure the account holds USDC.
  2. Open the asset list and select USDC; verify the symbol, icon, decimals, and fiat valuation are accurate and sourced from the native asset metadata.
  3. Ensure there is no duplicate Arc USDC entry and that totals include USDC exactly once.
  4. Open the Send screen for USDC and confirm the balance and token details are correct.

2. Stellar Snap - Send Flow Loader

Risk Level: MEDIUM

Why This Matters: Cherry-pick #46930 fixes a missing loader in Stellar send; without it, users may see frozen or confusing states during fee/address resolution.

Test Steps:

  1. Enable and set up the stellar-wallet-snap; open the Send flow selecting a Stellar account/asset.
  2. Enter a valid Stellar recipient and amount; verify a loading indicator appears during address/fee resolution.
  3. Confirm the loader dismisses when data is ready, the send button enables, and the flow completes without blank or frozen screens.
  4. Reopen Send and repeat to ensure no regression on subsequent attempts.

3. Money Transactions - Missing Transaction ID Handling

Risk Level: MEDIUM

Why This Matters: Cherry-pick #47054 fixes defaulting Money transaction ids to 0x0; missing ids previously could crash views or break history rendering.

Test Steps:

  1. Complete a Money deposit or withdrawal in an environment where the provider may omit a transaction id (or simulate via staging).
  2. Open the Activity details for the Money transaction and verify it shows a safe default (0x0) without crashing.
  3. Ensure explorer links are handled gracefully (disabled or generic) and the UI remains responsive.
  4. Perform another Money transaction with a normal tx id to confirm standard behavior is unaffected.

Release Scenarios (9)

High Risk Scenarios (5)

1. State Migrations - Full Profile Upgrade

Risk Level: HIGH

Why This Matters: Large changes across many migration scripts (including new helpers and new migrations 229–230) can corrupt user data or lose accounts/tokens during upgrade.

Test Steps:

  1. Start with an older MetaMask version (e.g., v10–v11) profile that has: multiple accounts (including an imported private key), a hardware wallet account, custom networks, several watch tokens/NFTs, and at least one pending transaction.
  2. Upgrade the same profile to 13.52.0 and unlock; observe any migration modal and allow it to complete.
  3. Verify all accounts are present, correct default network is restored, custom networks are intact, and watch tokens/NFTs are still listed with correct symbols/icons.
  4. Check total/fiat balances and per-token balances for accuracy, and ensure there are no duplicate or missing assets.
  5. Open Activity and ensure previous pending/confirmed/canceled transactions are preserved with correct statuses.

2. State Migrations - Pending/Failed Transaction Handling

Risk Level: HIGH

Why This Matters: New fail-tx migration logic can mislabel or lock transactions, breaking core send/confirm flows after upgrade.

Test Steps:

  1. On an older version, create a stuck/pending transaction (e.g., low gas on a test network) and close the extension.
  2. Upgrade to 13.52.0 and unlock, letting migration complete.
  3. Verify pending/stuck transactions are reclassified correctly (e.g., failed when appropriate) per the new fail-tx migration.
  4. Ensure UI remains responsive: Activity list loads, speed-up/cancel actions only appear when valid, and no infinite spinners.
  5. Attempt to submit a new transaction post-upgrade to ensure no residual migration state blocks normal operations.

3. Migration Modal Resilience and Recovery

Risk Level: HIGH

Why This Matters: A crash or reload during migration can corrupt state; new migration framework changes must safely resume or recover.

Test Steps:

  1. Trigger an upgrade to 13.52.0 with a profile requiring multiple migrations and wait at the migration modal.
  2. Force close/reload the extension mid-migration (e.g., browser restart).
  3. Reopen MetaMask, unlock, and verify the migration resumes or presents a safe retry path without data loss.
  4. Confirm final state shows all accounts/networks/assets intact and the app is functional.
  5. Review for any unexpected secondary migration prompts or errors.

4. Analytics/Metametrics Consent (Onboarding and Settings)

Risk Level: HIGH

Why This Matters: Major refactor around analytics/metametrics (controller and UI changes) can cause consent state regressions that block onboarding or break navigation.

Test Steps:

  1. Perform fresh onboarding to the metametrics consent screen; toggle consent ON and complete onboarding.
  2. In Settings > Security & Privacy, toggle analytics/consent OFF.
  3. Perform common actions (open portfolio, open send, switch networks) and verify no consent prompts reappear and navigation is not blocked.
  4. Lock and unlock the wallet; verify the consent state persists and the app remains stable.
  5. Re-enable consent in Settings and ensure typical flows remain unaffected.

5. Snaps Permissions Prompts and Enforcement

Risk Level: HIGH

Why This Matters: Changes to snaps permission specifications are security-sensitive; incorrect prompts or enforcement can leak account data or over-grant capabilities.

Test Steps:

  1. Install a Snap that requests multiple permissions (e.g., network access, notifications) and review the permission dialog text and scopes.
  2. Approve installation, then from the Snap trigger a request for account access and choose Deny; verify no account data is exposed and the Snap handles the error gracefully.
  3. In Settings > Snaps and Connected Sites, verify shown permissions match expected scopes; revoke a permission.
  4. Trigger the same Snap action again; verify a fresh permission prompt appears and the scopes are limited to the requested capability.
  5. Approve and confirm subsequent Snap actions function as expected without over-granting access.

Medium Risk Scenarios (4)

1. Assets Migration and De-duplication

Risk Level: MEDIUM

Why This Matters: Selector and migration updates for assets can surface duplicates or lose tokens, causing incorrect balances or confusion.

Test Steps:

  1. On an older version, add watch tokens with overlapping metadata or duplicates across multiple custom networks.
  2. Upgrade to 13.52.0 and unlock; let migrations complete.
  3. Verify the asset list shows one entry per token per network with correct symbols, icons, and decimals.
  4. Confirm fiat valuation and totals are correct and there are no ghost/duplicated assets.
  5. Remove and re-add a watch token to confirm stable behavior post-migration.

2. Asset List Network Filter and Labeling

Risk Level: MEDIUM

Why This Matters: Updated filter components and hooks can mislabel filters or show wrong totals, misleading users about their balances.

Test Steps:

  1. Ensure balances on at least two networks (e.g., Mainnet and Polygon) and open the asset list.
  2. Open the Network Filter modal and select only one network; apply.
  3. Verify the asset list and total balance reflect only the selected network, and the filter button label matches the selection.
  4. Clear the filter and confirm the label resets and totals recompute correctly.
  5. Toggle between networks quickly to ensure no stale UI or incorrect counts.

3. Account Group Balance Accuracy

Risk Level: MEDIUM

Why This Matters: Changes to balance aggregation components can yield incorrect totals, directly affecting user trust in displayed balances.

Test Steps:

  1. With multiple accounts holding tokens across networks, open the home/portfolio view and note the account group total.
  2. Apply a network filter to a subset of networks and verify the group total matches the sum of visible assets.
  3. Toggle primary currency (fiat/native) and ensure totals convert correctly without rounding drift.
  4. Add/remove a small-balance token and confirm totals update immediately without stale values.
  5. Reload the extension and verify displayed totals persist and remain accurate.

4. Rewards Panel and Claim Flow

Risk Level: MEDIUM

Why This Matters: Rewards-controller changes can break claim flows or show incorrect eligibility, affecting user funds and trust.

Test Steps:

  1. Open the Rewards or Portfolio section and verify rewards status/banners render without error.
  2. If eligible, start a claim; confirm the transaction and wait for confirmation.
  3. Verify the reward balance and history update correctly after claim.
  4. If not eligible, ensure the UI shows the correct gated state (disabled or explanatory text) with no broken actions.
  5. Navigate away and back to confirm state persistence.

Teams Sign-off Status

Signed off: None yet

Awaiting sign-off (8):
Accounts, Assets, Networks, Onboarding, Permissions, Settings, Snaps, Transactions


Generated by AI Test Plan Analyzer (gpt-5) at 2026-10-13T17:59:31.868Z

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

@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 (UI)

One accessibility issue in the DropdownEditor refactor — details inline.

Chrome extensions (app/)

No new MV3 or service-worker issues flagged in this release diff (background TS migration, offscreen Ledger WebHID probe, and stream cleanup look consistent with existing patterns).

Open in Web View Automation 

Sent by Cursor Automation: Modern web guidance

referenceElement={referenceElement}
position={PopoverPosition.Bottom}
role={PopoverRole.Dialog}

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.

ARIA role mismatch (listbox vs dialog): The trigger correctly exposes a listbox popup (aria-haspopup="listbox", aria-controls={listboxId}), and the menu surface uses role="listbox" / role="option". Wrapping that surface in the legacy Popover with role={PopoverRole.Dialog} makes the floating layer a dialog, which conflicts with the combobox/listbox pattern in modern web a11y guidance (interactive listbox should not live inside a modal dialog landmark).

Suggested fix: Drop the dialog role for this control—e.g. render the positioned surface without role="dialog" so only the inner listbox is exposed—or migrate to a combobox/listbox primitive where the popover panel’s top-level role is listbox (not nested in a dialog). If you keep listbox semantics, follow up with roving tabindex / arrow-key support on options.

This branch had an error being deployed

1 failed, 2 active, and 3 inactive deployments
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.52.0 Issue or pull request that will be included in release 13.52.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.