Skip to content

release: 13.41.0 - #44583

Merged
HowardBraham merged 125 commits into
stablefrom
release/13.41.0
Jul 27, 2026
Merged

HowardBraham merged 125 commits into
stablefrom
release/13.41.0

Conversation

@metamaskbot

@metamaskbot metamaskbot commented Jul 16, 2026 •

Copy link
Copy Markdown
Collaborator

🚀 v13.41.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
  • Extension Platform
  • MetaMask Delivery
  • Money Movement
  • Networks
  • Onboarding
  • Perps
  • Product Safety
  • Swaps and Bridge

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

Copilot AI and others added 30 commits July 11, 2026 06:43
…44294)

Fixes: https://github.com/MetaMask/MetaMask-planning/issues/7466
CHANGELOG entry: null

Derived values from multiple `useSelector` calls in the home balance hot
path were recomputed on every render, even when their dependencies
hadn't changed.

### Changes

- **`aggregated-balance.tsx`** — memoize `showNativeTokenAsMain`,
`isNonEvmRatesAvailable`, `formattedFiatDisplay`,
`formattedTokenDisplay`

- **`coin-overview.tsx` (`LegacyAggregatedBalance`)** — memoize
`showNativeTokenAsMain`, `isNotAggregatedFiatBalance`,
`balanceToDisplay`; convert `getCurrencyDisplayType()` inline function
to `useMemo`

- **`coin-overview.tsx` (`CoinOverview`)** — memoize
`isMetaMetricsEnabled`, `shouldShowBalanceEmptyState`; wrap
`handleSensitiveToggle` in `useCallback`

- **`aggregated-percentage-overview.tsx`** — consolidate all derived
display values (`amountChange`, `percentageChange`, formatted strings,
`color`) into a single `useMemo` in both `AggregatedPercentageOverview`
and `AggregatedMultichainPercentageOverview`

- **`aggregated-percentage-overview-cross-chains.tsx`** — wrap
`getPerChainTotalFiat1dAgo` (closes over `crossChainMarketData`) in
`useCallback`; consolidate derived cross-chain display values into a
single `useMemo`

- **`account-group-balance.tsx`** — memoize `caipChainId`, `isEvm`,
`isTestnetSelected`, `showNativeTokenAsMain`

### Example

```tsx
// Before: recomputed on every render
const showNativeTokenAsMain =
  showNativeTokenAsMainBalance && Object.keys(enabledNetworks).length === 1;

// After: only recomputed when deps change
const showNativeTokenAsMain = useMemo(
  () => showNativeTokenAsMainBalance && Object.keys(enabledNetworks).length === 1,
  [showNativeTokenAsMainBalance, enabledNetworks],
);
```

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a
summary for commit c8ad878. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: dddddanica <zhaodanica@gmail.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**

This PR completes the **"Sync with mobile"** flow in MetaMask Extension.
After pairing with MetaMask Mobile via QR code, the extension sends the
user's selected wallets and accounts to mobile in a secure, structured
format.

The extension is the **sender**; mobile is the **receiver**.

### What changed

- Built the full **wallet export payload** sent when sync is ready
(`sync-ready` message).
- Users pick which wallets to sync (SRP wallets and imported private-key
accounts only — hardware and Snap wallets are excluded).
- Account names, primary wallet flag, and hidden/pinned states are
included so mobile can restore the same setup.
- Added `QrSyncDataService` to build the export payload from the user's
account selection.
- Wired the Settings UI through to the controller end-to-end.

### Flow by phase

Each step shows what the **user sees (UI)**, what the **extension does
behind the scenes (Controller)**, and what **MetaMask Mobile** does.
They communicate through a secure relay (MWP).

---

#### Phase 1: `idle` → `displaying-qr` — Start pairing

```
User opens "Sync with mobile"
         │
         ▼
┌─────────────┐    create session     ┌──────────────┐
│     UI      │ ───────────────────►  │  Controller  │
│  (QR code)  │ ◄───────────────────  │              │
└─────────────┘    show QR code      └──────┬───────┘
                                              │
                                              │ init-sync-session
                                              ▼
                                       ┌──────────────┐
                                       │    Mobile    │
                                       │  (scans QR)  │
                                       └──────────────┘
```

**What happens:** User opens Settings → Sync with mobile. The extension
creates a session and shows a QR code. Mobile scans it to connect.

---

#### Phase 2: `awaiting-otp-input` — Enter verification code

```
Mobile shows a code          User types code
         │                          │
         ▼                          ▼
┌──────────────┐   submit OTP   ┌──────────────┐
│    Mobile    │ ◄───────────── │  Controller  │
│  (shows OTP) │                │              │
└──────────────┘                └──────┬───────┘
                                       │
                                       ▼
                                ┌─────────────┐
                                │     UI      │
                                │ (code entry)│
                                └─────────────┘
```

**What happens:** Mobile displays a one-time code. The user enters it in
the extension to confirm both devices are paired.

---

#### Phase 3: `awaiting-sync-offer` — Waiting for mobile

```
┌─────────────┐                  ┌──────────────┐
│     UI      │ ◄─────────────── │  Controller  │
│  (loading)  │   "validating…"  │  (validates  │
└─────────────┘                  │   OTP)       │
                                 └──────┬───────┘
                                        │
                          sync-offer    │
                                 ┌──────▼───────┐
                                 │    Mobile    │
                                 │ "ready sync" │
                                 └──────────────┘
```

**What happens:** The extension validates the code and shows a loading
screen. Mobile confirms it is ready to receive wallets.

---

#### Phase 4: `reviewing-sync-offer` — Choose wallets & enter password

```
┌─────────────┐  pick wallets +   ┌──────────────┐
│     UI      │  enter password   │  Controller  │
│ (wallet list│ ────────────────► │ (stores      │
│ + password) │                   │  selection)  │
└─────────────┘                   └──────────────┘
```

**What happens:** The user enters their MetaMask password and selects
which wallets to sync. Only syncable wallets are shown (SRP and imported
accounts). Hardware and Snap wallets are hidden.

---

#### Phase 5: `awaiting-sync-completion` — Sending wallets to mobile

```
┌─────────────┐                  ┌──────────────┐
│     UI      │ ◄─────────────── │  Controller  │
│  (loading)  │   "syncing…"     │  builds +    │
└─────────────┘                  │  encrypts    │
                                 │  wallet data │
                                 └──────┬───────┘
                                        │
                          sync-ready    │  (encrypted wallet data)
                                 ┌──────▼───────┐
                                 │    Mobile    │
                                 │  (imports)   │
                                 └──────────────┘
```

**What happens:** The extension packages the selected wallets
(encrypted), sends them to mobile, and shows a loading screen while
mobile imports.

---

#### Phase 6: `completed` — Done

```
                                 ┌──────────────┐
                                 │  Controller  │
                                 │  (marks done)│
                                 └──────┬───────┘
                          sync-completed│
                                 ┌──────▼───────┐
                                 │    Mobile    │
                                 │  (finished)  │
                                 └──────────────┘
                                        │
                                        ▼
                                 ┌─────────────┐
                                 │     UI      │
                                 │  (success)  │
                                 └─────────────┘
```

**What happens:** Mobile confirms import is complete. The extension
shows a success screen with how many wallets and accounts were synced.

---

#### Error / cancel paths: `failed` or `cancelled`

If something goes wrong (wrong code, timeout, user cancels, mobile
disconnects), the flow stops and the user is returned to the home
screen. No wallet data is sent unless sync reaches the `sync-ready` step
successfully.

---

### Updated `sync-ready` payload schema (`QrSyncReadyData`)

When the extension is ready to sync, it sends a `sync-ready` message.
The wallet data lives in the `data` field as a list of wallet entries:

```json
{
  "type": "sync-ready",
  "version": "1.0.0",
  "deadline": 1700000060000,
  "data": [
    {
      "type": "Mnemonic",
      "mnemonic": "<base64-encoded recovery phrase>",
      "name": "Wallet 1",
      "isPrimary": true,
      "groups": [
        { "groupIndex": 0, "name": "Account 1", "pinned": true },
        { "groupIndex": 2, "name": "Hidden Account", "hidden": true }
      ]
    },
    {
      "type": "PrivateKey",
      "privateKey": "<base64-encoded private key>",
      "name": "Imported Account"
    }
  ]
}
```

**Field notes:**

| Field | Meaning |
|---|---|
| `type: "Mnemonic"` | A Secret Recovery Phrase (SRP) wallet |
| `type: "PrivateKey"` | A single imported account |
| `mnemonic` / `privateKey` | Encoded secrets (not plain text) |
| `name` | Wallet or account display name |
| `isPrimary` | Marks the main SRP wallet (only one) |
| `groups` | Which accounts from that SRP wallet to restore |
| `groupIndex` | Account position in the wallet (Account 1 = 0) |
| `hidden` / `pinned` | UI state — only included when `true` |
| `deadline` | Time limit for mobile to accept the data |

## **Changelog**

CHANGELOG entry: Added the ability to sync selected wallets and accounts
from the extension to MetaMask Mobile via QR code pairing in Settings.

## **Related issues**

Fixes:

## **Manual testing steps**

1. Set `ADD_DEVICE_SYNC_ENABLED=true` in `.metamaskrc` and rebuild the
extension.
2. Load the extension and go to **Settings → Sync with mobile**.
3. On MetaMask Mobile, start the sync flow and scan the QR code shown in
the extension.
4. Enter the verification code from mobile into the extension.
5. Wait for the wallet selection screen to appear.
6. Enter your MetaMask password and select one or more wallets (SRP
and/or imported accounts).
7. Tap **Continue** and confirm the loading screen appears, then the
success screen.
8. On mobile, verify the imported accounts match the names and count you
selected in the extension.
9. Repeat with multiple wallets and confirm only syncable wallets appear
(no Ledger/Trezor/Snap wallets in the list).
10. Test cancellation: close the flow mid-way and confirm you return to
the home screen without errors.

## **Screenshots/Recordings**

### **Before**

N/A — new feature behind `ADD_DEVICE_SYNC_ENABLED` flag.

### **After**

<!-- Add screenshots/recordings of: QR screen, OTP entry, wallet picker,
loading, and success screen -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [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]
> **High Risk**
> Changes how mnemonics and private keys are exported and encoded for
mobile sync, which is security-critical and contract-sensitive for the
MWP payload.
> 
> **Overview**
> Completes the extension **Sync with mobile** export path by moving
secret packaging into a new **`QrSyncDataService`** and driving
selection by **account group IDs** instead of entropy IDs.
> 
> **`syncAccounts`** now calls
`QrSyncDataService:buildWalletExportEntries` and sends a revised
**`sync-ready`** envelope: `deadline` at the message level and `data` as
`Mnemonic` / `PrivateKey` entries with wallet names, `isPrimary`, and
per-account `groups` metadata (names, hidden/pinned). Controller state
tracks **`qrSyncSelectedAccountGroupIds`** and drops the old
selected/imported account ID fields.
> 
> The Settings **add-device** flow passes **`selectedAccountGroupIds`**
through to the background, filters the picker to syncable wallets (SRP,
HD keyring, imported private key), and uses **whole-wallet** checkboxes
only. QR sync timeouts are split into **`QR_SYNC_TIMEOUT_MS`**
(offer/completion/MWP session).
> 
> Messenger init registers **`QrSyncDataService`**; the QR sync
controller messenger delegates export to the data service, which talks
to account tree and keyring actions.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bbf9796. 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: Ganesh Suresh Patra <ganesh.patra@consensys.net>
Co-authored-by: Lionell Briones <llenoil@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
## **Description**

During custom mUSD convert, pending child transactions are created
alongside the parent “Converting to mUSD” row.

This PR hides these internal transactions, so only the top-level convert
row shows while the flow is pending.

## **Changelog**

CHANGELOG entry: Fixed extra pending row during mUSD conversion flow

## **Related issues**

Fixes:
https://consensyssoftware.atlassian.net/browse/CEUX-1170

## **Manual testing steps**

1. Open the extension Activity list (redesign).
2. Start a custom convert to mUSD flow and leave it pending/signing.
3. Confirm only one row appears: “Converting to mUSD” (no “Interaction
in progress” / “With USDC” sibling).
4. Confirm the convert details Summary still shows the internal
send/receive steps as before.

## **Screenshots/Recordings**

### **Before**
<img width="259" height="202" alt="image"
src="https://github.com/user-attachments/assets/97616dd9-0916-48fb-b8a9-f94bb8ab5999"
/>


### **After**

<img width="222" height="147" alt="image"
src="https://github.com/user-attachments/assets/1f24906b-7d5d-4e80-8a50-19ccda333475"
/>

## **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
- [ ] 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**
> Activity-list filtering only; convert details still use
required-transaction data elsewhere. Low risk of hiding unrelated txs
unless they are linked via `requiredTransactionIds`.
> 
> **Overview**
> Fixes duplicate pending rows during **mUSD convert** by treating
required child transactions as internal-only in the Activity list.
> 
> **`selectLocalTransactions`** now drops entries whose **id** or
**hash** appears in the parent’s `requiredTransactionIds` set (not just
hash), so unsigned/pending relay steps no longer show as separate rows
while the parent “Converting to mUSD” row is still pending. The same
internal-required filter is applied to **smart transactions**, including
type exclusions via `EXCLUDED_TRANSACTION_TYPES`.
> 
> **`musdRelayDeposit`** is added to `EXCLUDED_TRANSACTION_TYPES` so
relay deposit steps stay out of unified lists. Minor JSDoc was added for
`smartTransactionsListSelector`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0fd16c1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

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

## **Description**

Jira Link: https://consensyssoftware.atlassian.net/browse/TO-874

Renames the QR account-sync feature from "Add device" to "Sync accounts"
and promotes it from a nested settings sub-tab to a standalone top-level
`/sync-accounts` route with its own full-page layout and back button.
This is a naming/relocation refactor built on top of
`feat/qr-sync-controller`; no sync behavior changes.

## Changes
- **Routing**
- Replaced `ADD_DEVICE_ROUTE` (`/settings/add-device`) with top-level
`SYNC_ACCOUNTS_ROUTE` (`/sync-accounts`).
- Registered the new lazy-loaded `SyncAccounts` route in
`routes.component.tsx`, still gated behind
`getIsAddDeviceSyncEnabled()`.
- Hid the app header on the sync-accounts page via `hideAppHeader` in
`routes/utils.js`.
- **Page/component rename** — moved `ui/pages/settings/add-device-tab/`
→ `ui/pages/settings/sync-accounts/`, renaming `AddDeviceSettings` →
`SyncAccountsSettings`, `AddDeviceSettingsStep` → `SyncAccountsStep`,
and related types.
- **New `SyncAccountsTab`** — full-page `Page`/`Header`/`Content`
wrapper with a back button that resets the controller state
(`QrSyncController:resetState`) and navigates to `DEFAULT_ROUTE`.
- **Settings registry & search** — updated `settings-registry.ts` and
`search-config.ts` to point at the new route/labels (`addDevice` →
`syncAccounts`, `ADD_DEVICE_ITEMS` → `SYNC_ACCOUNTS_ITEMS`).
- **Localization** — renamed the `addDevice` message key to
`syncAccounts` ("Sync with mobile") in `en` and `en_GB`.
- **Tests** — added `sync-accounts-tab.test.tsx` (renders settings +
back button) and renamed/updated existing settings and component tests
to match new names.

<!--
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: Moved account-sync flow from settings sub-page to top
level route.

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to Menu > Settings > "Sync with mobile"
2. Click on the navigation link
3. Should redirect outside settings to a `/sync-accounts`
4. Back button should redirect back to settings page

## **Screenshots/Recordings**

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

### **Before**

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

### **After**


https://github.com/user-attachments/assets/d5a39fe1-bbf3-4d93-a8b6-42af54e5aa90


<!-- [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**
> Routing and naming refactor only; QR sync logic is unchanged and
remains feature-flagged.
> 
> **Overview**
> Renames the QR mobile account-sync feature from **Add device** to
**Sync accounts** and moves it off the nested settings path to a
standalone **`/sync-accounts`** route, still behind
`getIsAddDeviceSyncEnabled()`.
> 
> The former `add-device-tab` module becomes **`sync-accounts`**
(`SyncAccountsSettings`, `SyncAccountsStep`, etc.). A new
**`SyncAccountsTab`** wraps the flow in a full-page layout with a back
control that calls `QrSyncController:resetState` and returns to
**Settings**. Settings registry gains an **`externalRoute`** flag so
this tab stays in the settings menu but is mounted by the app router
(not the nested `/settings/*` router), with **`hideAppHeader`** updated
for the new path.
> 
> i18n drops **`addDevice`** across locales and adds **`syncAccounts`**
(“Sync with mobile”) in English locales; search/registry keys follow the
rename. Tests and Jest console baselines are updated for the new paths
and names—no sync controller behavior changes in this diff.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
562fd60. 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: Ganesh Suresh Patra <ganesh.patra@consensys.net>
Co-authored-by: lwin <lwin.kyaw@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
Adds structured error handling and error UI to the QR account-sync flow.
Instead of silently exiting when a sync session reaches a terminal
state, the flow now surfaces a dedicated error screen with retry/cancel,
and routes recoverable errors (expired QR, expired OTP, too many OTP
attempts) back to the relevant step so the user can recover in place.

## Changes

- **Error codes & routing** (`shared/constants/qr-sync.ts`)
- Added `QR_EXPIRED` and `OTP_ATTEMPTS_EXCEEDED` error codes and a
`QrSyncErrorCode` type.
- Added `QR_SYNC_ERROR_PHASE_OVERRIDES`, mapping certain error codes to
the step that should render: `QR_EXPIRED` -> QR scan,
`OTP_EXPIRED`/`OTP_ATTEMPTS_EXCEEDED` -> OTP input.
- **Controller error resolution** (`app/scripts/controllers/qr-sync/`)
- Refactored `#setError` to accept `{ error, code, message }` and
resolve the final code from the raw error via a new detector registry,
falling back to the provided code/message.
- Added `utils.ts` helpers: `MWP_REQUEST_EXPIRED_CODE`,
`isQrExpiredError`, `QR_SYNC_ERROR_DETECTORS`, and
`resolveQrSyncErrorCode` (maps the MWP `REQUEST_EXPIRED` error to
`QR_EXPIRED`).
- Added controller and utils unit tests for the expired-handshake paths.
- **Sync flow orchestration** (`sync-accounts-settings.tsx`)
- Replaced the auto-exit-on-terminal-phase effect with a rendered
`SyncError` screen for `CANCELLED`/`FAILED`.
- `renderStep` now derives an `effectivePhase` from the override map,
and a shared `handleRetry` resets controller state.
- **New `SyncError` component** — danger icon, title,
error-code-specific message (with a generic fallback), and Try again /
Cancel actions; exported from the components barrel.
- **`EnterVerificationCode`** — now driven by an `onRestart` prop
(parent resets the session instead of the component calling
`createSession`), reads `qrSyncError`, shows OTP-expired and
max-attempts messages, disables inputs and hides the countdown when
attempts are exhausted, and consolidates error text into a single
`errorMessage`.
- **`QrCodeScan`** — retains the last QR payload so an expired QR stays
visible instead of collapsing to a skeleton, and applies dim + blur on
expiry; removed the controller-error dimming/scan-error message path.
- **i18n** (`en`, `en_GB`) — added `add_device_error_*` strings,
`add_device_try_again`, and `enter_verification_code_max_attempts`;
removed the now-unused `qrCodeScanError`.
- **Tests** — new `sync-error.test.tsx`; updated
`enter-verification-code.test.tsx` (Redux provider + max-attempts case),
`qr-code-scan.test.tsx` (payload retention), and
`sync-accounts-settings.test.tsx` (override routing + retry/cancel).


<!--
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: QR Sync flow should now show step specific error and
global errors on a dedicated error view

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to Menu > Settings > "Sync with mobile"
2. Should redirect to QR Sync flow
3. Test QR sync expiry. Wait until QR expires
4. Restart Flow
5. Complete the QR Scan flow
6. Should show "OTP verification view"
7. Test OTP expiry. Wait until OTP expires
8. Should show OTP error.

Please refer to screenshot for the global error. 
Some known incomplete flows/behavior are not implemented here, but
another PR for controllers.
- There is a controller error on reset after QR Expiration, so it does
not show yet on QR scan page.
- OT Expiry is not handled on controller side yet so, resetting the QR
Sync after this error does not work properly yet
- Only UI for max OTP attempt error is added, not controller handling
yet.

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

QR Expired
<img width="608" height="933" alt="Screenshot 2026-07-07 at 9 32 11 PM"
src="https://github.com/user-attachments/assets/38ee10e9-e030-4080-8d7d-2f6abcf6e431"
/>


OTP Expired
<img width="611" height="931" alt="Screenshot 2026-07-07 at 9 33 37 PM"
src="https://github.com/user-attachments/assets/4af56756-d5eb-4c53-8f02-a3b062c4a637"
/>


Global Account Sync Error
<img width="613" height="934" alt="Screenshot 2026-07-07 at 9 27 07 PM"
src="https://github.com/user-attachments/assets/7f7d7441-3768-435a-9713-b62a844df98c"
/>



<!-- [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**
> Touches account-sync UX and controller error classification for wallet
pairing; mistakes could mis-route users or mishandle expired sessions,
but changes are mostly UI and error mapping with tests.
> 
> **Overview**
> Replaces **auto-exit on terminal QR sync phases** with in-flow error
handling: a new **`SyncError`** screen (Try again / Cancel) for
`CANCELLED`/`FAILED`, and **`QR_SYNC_ERROR_PHASE_OVERRIDES`** so
recoverable codes (`QR_EXPIRED`, `OTP_EXPIRED`, `OTP_ATTEMPTS_EXCEEDED`)
still render the QR or OTP step instead of the generic error view.
> 
> **`QrSyncController`** now resolves error codes via
**`resolveQrSyncErrorCode`** / **`isQrExpiredError`**, mapping MWP
**`REQUEST_EXPIRED`** to **`QR_EXPIRED`**. **`EnterVerificationCode`**
uses parent **`onRestart`**, Redux **`qrSyncError`**, and max-attempts
messaging; **`QrCodeScan`** keeps the last QR visible on expiry
(dim/blur) and drops the old scan-error path. English i18n adds
**`add_device_error_*`** strings and removes **`qrCodeScanError`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
08ff1c2. 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: Ganesh Suresh Patra <ganesh.patra@consensys.net>
Co-authored-by: lwin <lwin.kyaw@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
Use CAIP-19 paths for fungible asset navigation and deep links, resolve
wallet and metadata-backed tokens on the asset page, and harden route
building against invalid ids and malformed URL params.

<!--
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:  migrate asset routes to CAIP-19 identifiers

## **Related issues**

Fixes:

## **Manual testing steps**

Try visiting tokens that you have networks you have connected, and
chains you don't have connected.
This should allow users to now visit assets they do not own (but have
network for). Defaults to home screen if a user does not have a valid
network.

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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



https://github.com/user-attachments/assets/32d4caf9-61ff-4f11-96af-bfb92e2ac05b



## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core navigation, deep-link interstitial policy for unsigned
asset URLs, and external token metadata fetching; mis-routing or bypass
behavior would be user-visible across chains.
> 
> **Overview**
> Fungible asset URLs move from hex chain id + contract address to
**CAIP-19** paths (`/asset/{caip-chain}/{encoded-asset-id}`), with
shared helpers in `shared/lib/asset-route` for building paths, decoding
route params (including Chrome vs Firefox fragment behavior), and
normalizing lookups for EVM, Solana, Tron, and NFT-style routes.
> 
> **Deep links and in-app navigation** now generate and expect those
paths (`buildAssetRoutePath`), including the `/asset?assetId=…` handler.
**`/asset` is added to the deep-link interstitial bypass list** so
unsigned asset links open directly, aligned with mobile; deferred
deep-link handling is covered by tests.
> 
> The **asset details page** resolves holdings via
`getFungibleAssetForRoute` and, when the user does not own the token,
loads display metadata through `buildTokenFromCaipAssetId` /
`useRouteAssetToken` (with loading and redirect-on-failure). Token list,
bridge flows, and e2e bridge navigation are updated to pass and wait on
CAIP-19 routes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cbcf34b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#44383)

The google.svg file was removed in #44227 as part of a legacy icon
cleanup. However, it is still actively referenced by the onboarding
login options and reveal-SRP list flows.

- Restore the original Google brand icon SVG
- Move it from app/images/icons/ to app/images/ to keep it separate from
the legacy icon set that is being deprecated
- Update all src references in login-options.tsx and reveal-srp-list.tsx

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

JIRA LINK : https://consensyssoftware.atlassian.net/browse/TO-896

## **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: restore google.svg and relocate to app/images/

## **Related issues**

Fixes:
- #44334
- #44340

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

<img width="1333" height="991" alt="Screenshot 2026-07-13 at 12 33
06 PM"
src="https://github.com/user-attachments/assets/ad9b782b-fc83-4185-9045-2382ae424048"
/>

<img width="1287" height="990" alt="Screenshot 2026-07-13 at 12 40
53 PM"
src="https://github.com/user-attachments/assets/13f821f4-93d7-40a2-8de9-5267cfc16965"
/>


## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Static asset and path-only UI updates with no auth, data, or
business-logic changes.
> 
> **Overview**
> Restores the **Google brand icon** that was removed during legacy icon
cleanup but is still required for onboarding and account flows.
> 
> Adds `app/images/google.svg` (outside the deprecated `icons/` tree)
and updates image `src` paths in **login-options** and
**reveal-srp-list** from `images/icons/google.svg` to
`images/google.svg` so the Google login button and SRP reveal list show
the icon again.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
574bb38. 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>
## **Description**

This PR wires the `'tron'` case into `withFixtures` and adds a smoke
script so a Tron local node can be started and consumed from a test
fixture. It is one reviewable step of a linear stack and replaces
#44139. Together with PRs 03-05 it supersedes #43722. Based on a fresh
`main` and under 1000 changed lines.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Part of the local-blockchain E2E initiative (WPN-536).
Replaces #44139; together with the preceding Tron node PRs (config,
seeder, bootstrap) supersedes #43722.

## **Manual testing steps**

1. `yarn build:test`
2. Run the Tron fixtures smoke script and confirm the `'tron'` fixture
case boots a local node and tears it down cleanly.

## **Screenshots/Recordings**

N/A — test infrastructure only, 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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only fixture and smoke tooling; the Anvil guard is a small
behavioral fix for mixed local-node fixtures.
> 
> **Overview**
> E2E fixtures can now boot a **Tron** local node via `localNodeOptions:
'tron'` (or `{ type: 'tron', options: ... }`), using the existing
`TronNode` seeder alongside the Anvil path.
> 
> **Accounts API v5** native-balance sync from `localNodes[0]` is
limited to **Anvil** only, so a Tron-first fixture no longer calls
`getBalance()` on a non-EVM node.
> 
> A new **`test:e2e:smoke:local-node-ports`** script starts two
java-tron networks on spaced ports, hits `getnowblock`, and tears
down—sanity check for port allocation and multi-node startup.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c91ac63. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Fixes: https://github.com/MetaMask/MetaMask-planning/issues/7467
CHANGELOG entry: null

Confirmation-flow hooks and components were creating new object
references and inline selector lambdas on every render, causing
unnecessary downstream re-renders and Redux re-subscriptions.

## Changes

### `useGasFeeToken.ts`
- Replace inline `(state) => selectTransactionAvailableBalance(state,
id, chainId)` lambda with a `useMemo`-created stable selector
- Wrap `useNativeGasFeeToken` and `useGasFeeToken` return objects in
`useMemo`; extract `transferTransaction` into its own `useMemo`

### `smart-transactions-banner-alert.tsx`
- Hoist `alertEnabled` inline typed selector lambda to a stable
`selectAlertEnabled` function outside the component
- Convert `getMarginStyle()` (called at render time) to a
`useMemo`-computed `marginStyle` value

### `transaction-details-account-row.tsx`
- Extract parameter-free selectors (`selectAccountGroupId`,
`selectFirstAccountGroupName`) as stable module-level functions
- Replace two parameter-dependent inline selector lambdas with
`useMemo`-created selectors keyed on `from` and `selectedAccountGroupId`
respectively
- Wrap `displayName` derivation in `useMemo`

```ts
// Before — new function reference every render
const balance = useSelector((state) =>
  selectTransactionAvailableBalance(state, transactionId, chainId),
);

// After — stable reference; recreated only when deps change
const selectBalance = useMemo(
  () => (state: unknown) =>
    selectTransactionAvailableBalance(state, transactionId, chainId),
  [transactionId, chainId],
);
const balance = useSelector(selectBalance);
```


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Render/identity optimizations only; displayed account names, gas fee
tokens, and banner visibility logic are unchanged.
> 
> **Overview**
> Reduces unnecessary re-renders in the confirmation flow by stabilizing
Redux subscriptions and object identities.
> 
> **`useGasFeeToken.ts`** — Parameterized `useSelector` callbacks are
built with `useMemo` (e.g. balance by `transactionId`/`chainId`).
`transferTransaction` and the objects returned from `useGasFeeToken` /
`useNativeGasFeeToken` are wrapped in `useMemo` so consumers do not see
new references every render.
> 
> **`transaction-details-account-row.tsx`** — Stateless selectors
(`selectAccountGroupId`, `selectFirstAccountGroupName`) and
`getMultichainAccountsState` move to module scope. Selectors that depend
on `from` or `selectedAccountGroupId` use `useMemo`; `displayName` is
memoized from the resolved names.
> 
> **`smart-transactions-banner-alert.tsx`** — The smart-transactions
migration alert enabledness check is hoisted to a stable
`selectAlertEnabled` used with `useSelector` instead of an inline
lambda.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3de6688. 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: dddddanica <zhaodanica@gmail.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**

Bump Snaps packages to enable `Blob` global in the execution
environment.

## **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: Add support for `Blob` global in Snaps

## **Related issues**

https://consensyssoftware.atlassian.net/browse/WPC-1126

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the isolated iframe where all Snaps run and bumps core Snaps
libraries, so regressions could affect every installed snap; scope is
limited to a coordinated version bump with no extension app logic
changes.
> 
> **Overview**
> Bumps Snaps platform dependencies so the extension loads the
**11.2.0** hosted iframe execution environment instead of **11.1.1**,
aligning runtime with the new package versions.
> 
> **`package.json`** updates `@metamask/snaps-execution-environments` to
`^11.2.0` and `@metamask/snaps-utils` to `^12.4.0`; **`yarn.lock`**
reflects the resolved tree (including transitive bumps on
snaps-sdk/superstruct inside execution-environments). **`builds.yml`**
sets `IFRAME_EXECUTION_ENVIRONMENT_URL` to
`https://execution.metamask.io/iframe/11.2.0/index.html` for **main**,
**beta**, **experimental**, and **flask** so each build type points at
the matching remote iframe.
> 
> Per the PR description, this enables the **`Blob` global** inside the
Snaps execution environment for snaps that rely on it.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c334097. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
… comparison (#44400)

## **Description**

`trackImportEvent` was not being used properly in failure scenarios and
it's string comparison against strategy type was wrong.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

1. Run tests
2. Confirm they pass

## **Screenshots/Recordings**

N/A

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

- [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**
> Analytics-only fix in the import account UI with no auth or import
logic changes beyond event payloads.
> 
> **Overview**
> Fixes **MetaMetrics** tracking when account import fails in
`import-account.js`. On catch, `trackImportEvent` now receives
**`false`** instead of the error message string, so failures emit
**`AccountAddFailed`** instead of being misclassified as success.
> 
> **`trackImportEvent`** now compares `strategy` to **`'privateKey'`**
(not `'Private Key'`), matching the values passed from the import flow,
so **`account_import_type`** is **`PrivateKey`** vs **Json** on
failures.
> 
> Tests inject a mock **`trackEvent`** and assert failed private-key and
JSON imports fire **`AccountAddFailed`** with the expected
**`account_import_type`** and related properties.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a65c69d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#44386)

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

Add Cursor Skill to automatically add new EVM networks to swaps

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

## **Changelog**

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

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

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

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-4775

## **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**
> Documentation-only; no product code, auth, or data-path changes.
> 
> **Overview**
> Adds **agent-facing documentation** for onboarding new **EVM** chains
into the **unified swaps/bridge** flow, mirroring the existing non-EVM
standard pattern.
> 
> **`docs/add-evm-swaps-bridge-network.md`** is the SSOT: prerequisites
(`network.ts`, `@metamask/bridge-controller`), the two-layer allowlist
(hard list + `bridgeConfigV2.chainRanking`), step-by-step edits
(`shared/constants/bridge.ts`, `ui/pages/bridge/utils/stablecoins.ts`,
LaunchDarkly `bridgeConfigV2`), optional popular-network enablement,
validation checklist, and targeted unit tests (`selectors.test.ts`,
`useSmartSlippage.test.ts`). Reference PRs called out are MegaETH and
Robinhood Chain.
> 
> **`AGENTS.md`** gains an **EVM Swaps/Bridge Agent Entrypoints**
section linking that doc and the Cursor skill at
`.cursor/skills/mms-add-evm-swaps-bridge-network/SKILL.md`.
> 
> No runtime bridge or swaps code changes in this diff—documentation and
discoverability for agents only.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
dfb93f5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Removes the headless `smartTransaction:showSmartTransactionStatusPage`
approval mechanism. The approval was created inside
`submitSmartTransactionHook`/`submitBatchSmartTransactionHook` and
immediately resolved by a UI-side shim with no more UI consuming it
after removing the post STX page.

Removed:
- Approval request creation, update, and end-flow in
`smart-transactions.ts`
- `useResolveSmartTransactionApprovals` 
- STX filter in `selectPendingApprovalsForNavigation`
- STX exclusion in `getAttentionRequiredApprovalCount`
- STX rate-limit exemption in `getApprovalControllerInstanceOptions`
- STX pending-approval rejection loop in `resetAccount` (there is no
matching approval to reject)
- Now-unused constants


## **Changelog**

CHANGELOG entry: null

## **Related issues**

<!--
Fixes:
-->

## **Manual testing steps**

1. Submit a smart transaction
2. Verify the transaction submits, completes, and the existing toast
lifecycle still fires

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

### **Before**

### **After**
-->

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches smart transaction submit hooks and approval/navigation
behavior; incorrect removal could affect badges or toasts, though
transaction toasts remain on `useTransactionEventToasts`.
> 
> **Overview**
> Removes the headless
**`smartTransaction:showSmartTransactionStatusPage`** approval path that
was created during smart transaction submit and auto-resolved in the UI
with no confirmation screen.
> 
> **`SmartTransactionHook`** no longer calls
`ApprovalController:addRequest` / `updateRequestState`, drops
`shouldShowStatusPage` and transaction-type gating for that flow, and
the hook messenger no longer depends on approval actions. Submit/batch
still wait for hashes via **`SmartTransactionsController`** events.
> 
> Related cleanup: **`SMART_TRANSACTION_CONFIRMATION_TYPES`** and UI
helpers (`useResolveSmartTransactionApprovals`,
`useSmartTransactionToasts`, `selectSmartTransactions`, toast type
exclusions) are deleted. **`getAttentionRequiredApprovalCount`** counts
all pending approvals; navigation no longer filters STX status approvals
or the skip feature flag. **`resetAccount`** no longer rejects matching
STX pending approvals; approval rate-limit exclusions and tests are
updated accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f6ded04. 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
## **Description**

Migrates remaining legacy `MetaMetricsContext.trackEvent` /
`trackMetaMetricsEvent` call sites in the **Web3Auth (recovery phrase,
passkey, consent, password)** domain to `useAnalytics()` +
`createEventBuilder` (or `trackAnalyticsEvent` for Redux thunks).

Part of umbrella tracker #43885 (**15d · Web3Auth onboarding gaps**).

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Build and load the extension (`yarn start`).
2. Exercise the flows touched by this PR (see changed files).
3. With MetaMetrics debug enabled, confirm events still fire with the
same names and properties.

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

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Analytics instrumentation refactor only; no changes to wallet, auth,
or password/passkey business logic beyond how events are emitted.
> 
> **Overview**
> Replaces remaining **`MetaMetricsContext`** `trackEvent` usage in
Web3Auth-related UI (basic functionality modal, change password,
marketing consent, passkey troubleshoot, reveal SRP) with
**`useAnalytics()`** and **`createEventBuilder().build()`**, keeping the
same event names and properties.
> 
> **Passkey troubleshoot** now pulls **page title** for support-link
events from **`useSegmentContext`** instead of the legacy
second-argument context merge.
> 
> Tests for **reveal recovery phrase** (and related setup) mock
**`useAnalytics`** and assert **`trackEvent`** receives the built
payload shape (`name`, `properties`, `sensitiveProperties`) on SRP
export success and failure paths.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
47b7b93. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

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

## **Description**

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

Move status-icon so we can restore the "ui/icon" path in the list

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

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**
> Import-path and lint-rule changes only; no behavior change to
StatusIcon, toast, or activity UI beyond module location.
> 
> **Overview**
> **Moves `StatusIcon` out of the deprecated `ui/icon` tree** into
`ui/status-icon/status-icon`, and updates imports in toast, pending
activity rows, and the toast unit test mock to match.
> 
> **Re-enables the fitness rule** that treats `ui/icon` as a blocked
import path in `prevent-deprecated-imports.ts` (it had been commented
out while `status-icon` still lived under `ui/icon`).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ecb7db9. 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…#44408)

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

Change `CODEOWNERS` to bring outstanding `ramps` code under [team
money-movement](https://github.com/orgs/MetaMask/teams/money-movement)

<!--
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:
[TRAM-3745](https://consensyssoftware.atlassian.net/browse/TRAM-3745)


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


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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Repository governance only; no runtime or product behavior changes.
> 
> **Overview**
> Expands **Money Movement** ownership in `.github/CODEOWNERS` so more
ramps-related paths require `@MetaMask/money-movement` review, alongside
the existing `**/ramps/**` rule.
> 
> Adds explicit patterns for `ui/hooks/ramps/`,
`ui/selectors/rampsController/`,
`ui/store/controller-actions/ramps-controller*`,
`app/scripts/messenger-client-init/ramps-*` and `messengers/ramps-*`,
and `test/e2e/tests/btc/mocks/ramps.ts`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c0d234c. 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**
Upgrades React and React DOM from v17 to v18 and cleans up related
legacy patterns across the extension.

- Bump `react` and `react-dom` from `^17.0.2` to `^18.2.0`
- Remove the React 17 webpack `react/jsx-runtime` alias workaround in
`development/webpack/webpack.config.ts`
- Update LavaMoat policies for the dependency change
- Remove `LegacyRouteMessengerProvider` and update `routes/test` helpers
to use context-based providers
- Replace deprecated defaultProps on function components with default
parameters (UI components, `test/storybook` I18nProvider copies)
- Fix async test patterns (`useCarouselManagement`, integration alerts,
etc.)
- Refactor `@rive-app/react-canvas` jest mock

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

## **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]
> **High Risk**
> A core UI runtime upgrade touches the entire extension surface;
regressions would affect rendering, concurrency, and user flows even
though most code changes are tests and build policy.
> 
> **Overview**
> Upgrades **`react`** and **`react-dom`** from v17 to **v18.2.0**,
which drives the rest of the diff: build, supply-chain policy, and test
harness updates so CI and E2E stay stable under React 18’s rendering and
async behavior.
> 
> **Build & LavaMoat:** Drops the webpack **`react/jsx-runtime`** alias
that only existed for React 17 ESM resolution. Regenerates LavaMoat
policies so **`react`**, **`react-dom`**, and **`scheduler`** match the
new package graph (e.g. **`object-assign`** paths, scheduler
globals/packages).
> 
> **Tests & tooling:** Integration setup mocks **Rive** `RuntimeLoader`
so `act()` does not hang in jsdom; several notification/confirmation
integration tests stop wrapping everything in **`act()`** and rely on
**`waitFor`/`findBy`**. E2E helpers handle stale DOM nodes, drawer
motion, send-alert modals, and notification flows. Unit/UI fixes include
**`CheckBox`** **`aria-checked`**, **`useCountdownTimer`** functional
updates, lighter **`useTokenSearchResults`** tests, and refreshed Jest
console/snapshot baselines.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
421b31f. 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: MetaMask Bot <metamaskbot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…44373)

## **Description**

The `enableMV3TimestampSave` debug preference is obsolete now that MV3
keep-alive runs unconditionally from service worker startup (see
[comment](#44348 (comment))).
This PR removes the preference from controller state, UI, and persisted
fixtures, and adds migration 217 to drop the field from existing
profiles.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

Related: #44348
Related: #43773

## **Manual testing steps**

1. Run `yarn start` and load the extension in Chrome.
2. Open **Settings → Advanced → Developer options** and confirm the
**Service worker keep-alive** toggle is no longer shown.
3. Unlock MetaMask and confirm normal background behavior is unchanged.
4. (Optional) Upgrade from a profile that had `enableMV3TimestampSave:
false` and confirm migration 217 removes the field without errors.

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

### **Before**

### **After**
-->

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## **Description**

### Context

Fixes
[#44068](#44068) —
an intermittent E2E failure in `reset-wallet.spec.ts` during the
**second** onboarding pass after wallet reset. The failure surfaces as:

```
OnboardingFlow: failed to create new account Error: Keyring not found
Error creating password Error: Keyring not found
```

`Keyring not found` is thrown from
`KeyringController.exportSeedPhrase()` when the HD keyring has been
cleared from memory mid-export (`keyrings[0]?.keyring` is falsy). On the
create-wallet path this happens via `createNewVaultAndGetSeedPhrase` →
`getSeedPhrase` → `exportSeedPhrase`.

### Root cause (confirmed)

This was **reproduced locally** with the same error signature as CI.

MetaMask can have **multiple UI surfaces** open at once (main window +
side panel). They share one background, but each has its **own Redux
store**.

On second-pass onboarding password submit, the main window:

1. `createNewVault` — creates the vault and unlocks it
2. `getSeedPhrase` — exports the recovery phrase

Previously these were **two separate background RPCs** with
`createVaultMutex` released between them. Meanwhile, a **stale side
panel** (left open after first onboarding) could react to `isUnlocked:
true` while onboarding was still incomplete. Its routing reaches the
onboarding **lock trap** (`OnboardingFlowSwitch` → `LOCK_ROUTE` →
`setLocked`), which clears in-memory keyrings **during** step 2 →
`Keyring not found`.

**Why it flakes in CI / reset-wallet E2E:** The spec runs reset and
second onboarding back-to-back with no human delay. The side panel can
lag on `/unlock` or `/` while the main window races ahead to password
submit. The dangerous window also exists in production whenever crypto
work widens the gap between create and export, or when any new surface
boots at `/` during export.

**In short:** the main window exports the seed phrase while another
surface locks the wallet.

### Fix

**1. Background — atomic vault + export under one mutex**

- Added `createNewVaultAndGetSeedPhrase(password)` — holds
`createVaultMutex` through vault creation **and** seed export.
- Added `unlockAndGetSeedPhrase(password)` — same mutex through unlock +
export (import rehydration path).
- Refactored vault creation into
`_createNewVaultAndKeychainUnderLock(password)`;
`createNewVaultAndKeychain` delegates to it (behavior unchanged).
- Passed `createVaultMutex` into `LegacyBackgroundApiService` so
**`setLocked` also acquires it** — lock requests wait until vault
create/export completes.

**2. UI — single RPC thunks**

- `createNewVaultAndGetSeedPhrase` → one background call (was
`createNewVault` + `getSeedPhrase`).
- `unlockAndGetSeedPhrase` → one background call (was `submitPassword` +
`getSeedPhrase`).
- `createNewVaultAndSyncWithSocial` → uses
`createNewVaultAndGetSeedPhrase`, then social backup.

**3. UI — side panel mitigation**

- Added `useCloseSidePanelOnWalletReset` (wired in
`routes.component.tsx`).
- When the side panel sees `isWalletResetInProgress` from shared
background state, it calls `window.close()` so a stale panel cannot
enter the onboarding lock trap during second-pass onboarding.

**4. UI — duplicate submit guard**

- `create-password.tsx`: `isSubmitting` state + `loading` on the form to
block double password submit.

**5. Tests**

- `metamask-controller.actions.test.js` —
`createNewVaultAndGetSeedPhrase`, `unlockAndGetSeedPhrase`.
- `ui/store/actions.test.js` — single-RPC thunks, updated social-create
path.
- `ui/hooks/useCloseSidePanelOnWalletReset.test.ts` — side panel close
behavior.
- `create-password.test.tsx` — submit guard.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: #44068

## **Manual testing steps**

1. Build a test extension: `yarn build:test`
2. Run the failing E2E spec (repeat to check for flakiness):
   ```bash
yarn test:e2e:single test/e2e/tests/reset-wallet/reset-wallet.spec.ts
--browser=chrome
   ```
3. **Standard create-wallet onboarding**
   - Fresh profile → complete onboarding with new SRP.
   - Confirm password creation succeeds and SRP backup appears.
4. **Reset-wallet flow (main repro scenario)**
- Complete first onboarding (side panel left open from onboarding
completion).
- Lock wallet → Forgot password → "I don't know my Recovery Phrase" →
Reset wallet.
   - Complete second onboarding with a new password.
   - Confirm onboarding completes without `Keyring not found`.
   - Confirm side panel closes when reset starts (if still open).
5. **Import unlock path**
   - Start import onboarding, submit password on unlock step.
   - Confirm no `Keyring not found` during seed retrieval.
6. Confirm create-password submit is disabled while the request is in
flight (double-click does not fire a second submission).

<!--
## **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]
> **High Risk**
> Changes vault creation, unlock, seed export, and locking serialization
across background and UI—security-sensitive keyring paths where races
previously caused data-loss-class failures.
> 
> **Overview**
> Fixes an intermittent **Keyring not found** failure during second-pass
onboarding when vault creation and seed export were separate RPCs and
another UI surface could lock the wallet in between.
> 
> The background now exposes **`createNewVaultAndGetSeedPhrase`** and
**`unlockAndGetSeedPhrase`**, each holding **`createVaultMutex`**
through vault work and seed export.
**`LegacyBackgroundApiService:setLocked`** also acquires that mutex so
locks wait until create/export finishes. UI thunks call the single-RPC
methods instead of create/unlock then **`getSeedPhrase`**, with shared
**`encodeSeedPhraseForBackground`** /
**`decodeSeedPhraseFromBackground`** for seed bytes over the port.
> 
> **`useCloseSidePanelOnWalletReset`** closes the side panel when
**`isWalletResetInProgress`** is set so a stale panel cannot hit the
onboarding lock trap. Create-password adds **`isSubmitting`** / form
**`loading`** to block double submit.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a740b42. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…scriptions to parent (#44295)

Fixes: https://github.com/MetaMask/MetaMask-planning/issues/7465
CHANGELOG entry: null


Each row in the token list independently subscribed to `getMarketData`,
`getCurrencyRates`, and `getNetworkConfigurationsByChainId` — triggering
O(n) Redux subscriptions for data that is identical across all rows.
This batch lifts those reads to the parent and memoizes derived values
throughout the token list hot path.

## `token-list-item.tsx` — conditional selector lift

Added optional `marketData`, `currencyRates`, `networkConfigurations`
props. When the parent provides them, the component switches to a stable
no-op selector so it does not subscribe to those Redux slices at all:

```ts
// Module-level — typed to match the real selector so useSelector infers the
// return type without unsafe `as` casts
const EMPTY_MARKET_DATA: MarketDataMap = {};
const selectEmptyMarketData: typeof getMarketData = () => EMPTY_MARKET_DATA;

// Inside the component — boolean flag keeps the useMemo dep array honest
// without referencing the object reference (which changes every render) and
// without an eslint-disable comment (which blocks React Compiler)
const isMarketDataPropProvided = marketDataProp !== undefined;
const marketDataSelector = useMemo(
  () => (isMarketDataPropProvided ? selectEmptyMarketData : getMarketData),
  [isMarketDataPropProvided],
);
const marketDataFromStore = useSelector(marketDataSelector);
const multiChainMarketData = marketDataProp ?? marketDataFromStore;
```

Fully backwards-compatible — all new props are optional; callers that
don't pass them fall back to the existing direct `useSelector` path.

## `token-list.tsx` — stable callbacks

Wrapped `handleTokenClick` and `renderTokenListItem` in `useCallback`
with correct dependency arrays. Inlined the inner `renderTokenCell`
helper to eliminate an unnecessary closure layer.

## `percentage-and-amount-change.tsx` — stale-closure fix

The `useMemo` dep array was `[marketData]` while the callback also
captured `balanceValue`, `conversionRate`, `currentChainId`,
`fiatCurrency`, and `nativeCurrency`. The computed balance-change value
would not update when those variables changed.

## `network-filter.tsx` — memoized derived values

`handleFilter` wrapped in `useCallback`; `allAddedPopularNetworks` and
`filter` wrapped in `useMemo` to stabilise references passed to
children.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Performance and memoization-only changes with a backwards-compatible
optional-prop API; the percentage change fix corrects stale UI rather
than altering business rules.
> 
> **Overview**
> This PR reduces redundant work on the home **token list** and related
UI by stabilizing callbacks/derived values and preparing rows to avoid
**O(n) Redux subscriptions** for shared global data.
> 
> **`TokenListItem`** gains optional `marketData`, `currencyRates`, and
`networkConfigurations` props. When a parent supplies them, each row
switches to stable no-op selectors so it does not subscribe to
`getMarketData`, `getCurrencyRates`, or
`getNetworkConfigurationsByChainId`; otherwise behavior stays the same
via store fallbacks.
> 
> **`token-list.tsx`** wraps `handleTokenClick` and
`renderTokenListItem` in `useCallback` and inlines the former
`renderTokenCell` helper so virtualized list render props stay stable.
> 
> **`percentage-and-amount-change.tsx`** expands the `balanceChange`
`useMemo` dependency list so fiat/balance/conversion inputs actually
refresh the computed change (previously only `marketData` was listed).
> 
> **`network-filter.tsx`** memoizes `handleFilter`,
`allAddedPopularNetworks`, and the effective `filter` object passed to
children.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5486ab2. 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: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: dddddanica <zhaodanica@gmail.com>
## **Description**

Migrates remaining legacy `MetaMetricsContext.trackEvent` /
`trackMetaMetricsEvent` call sites in the **Platform (metametrics
toggle, clear data, A/B test hook)** domain to `useAnalytics()` +
`createEventBuilder` (or `trackAnalyticsEvent` for Redux thunks).

Part of umbrella tracker #43885 (**15e · Platform metrics UI**).

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Build and load the extension (`yarn start`).
2. Exercise the flows touched by this PR (see changed files).
3. With MetaMetrics debug enabled, confirm events still fire with the
same names and properties.

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

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Refactor-only analytics instrumentation with tests locking event
shape; no changes to metrics opt-in, deletion tasks, or user data
handling behavior.
> 
> **Overview**
> **Platform settings analytics** in the clear-metrics modal and
participate-in-metrics toggle now go through `useAnalytics()` and
`createEventBuilder` instead of `MetaMetricsContext.trackEvent`. Event
names, categories, properties, and options such as
`excludeMetaMetricsId` on deletion flows are unchanged—only the wiring
API differs.
> 
> Tests mock `useAnalytics` and assert the built event payloads for
enable/disable toggles, deletion requests, and deletion failures. The
shared actions mock adds a no-op `trackAnalyticsEvent` for Redux/thunk
callers.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1e1609b. 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>
## **Description**

Migrates remaining legacy `MetaMetricsContext.trackEvent` /
`trackMetaMetricsEvent` call sites in the **Settings (smart account
suggestion and smart transactions opt-in thunks)** domain to
`useAnalytics()` + `createEventBuilder` (or `trackAnalyticsEvent` for
Redux thunks).

Part of umbrella tracker #43885 (**15g · Settings Redux thunks**).

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Build and load the extension (`yarn start`).
2. Exercise the flows touched by this PR (see changed files).
3. With MetaMetrics debug enabled, confirm events still fire with the
same names and properties.

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

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Mechanical telemetry refactor with unchanged event names and
properties; preference dispatch logic is untouched.
> 
> **Overview**
> Continues the analytics migration in **Settings Redux thunks** by
replacing legacy `trackMetaMetricsEvent` payloads with
`trackAnalyticsEvent` + `createEventBuilder` in `ui/store/actions.ts`.
> 
> **`setDismissSmartAccountSuggestionEnabled`** and
**`setSmartTransactionsPreferenceEnabled`** still emit `SettingsUpdated`
under the Settings category with the same property keys
(`dismiss_smt_acc_suggestion_enabled` / `prev_*` and `stx_opt_in` /
`prev_stx_opt_in`); only the construction path changes. The file now
imports `createEventBuilder` (not just types) from
`shared/lib/analytics/create-event-builder`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cdabb3b. 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**

MV3 keep-alive timestamp polling previously started in `background.js`,
only after persisted state was loaded. That left a gap during early
service worker startup, before the dynamic `background.js` import
finished.

This change moves the existing `saveTimestamp` + `setInterval` logic
into the MV3 service worker entry points (`service-worker.ts` for
webpack dev, `app-init.js` for browserify prod/E2E) so polling starts at
module load.

Install `event.waitUntil` for background script loading is handled
separately in #44189.

Removal of the obsolete `enableMV3TimestampSave` debug preference is
tracked in #44373.

1. What is the reason for the change?
- Keep-alive activity should begin as early as possible in the MV3
service worker lifecycle.
2. What is the improvement/solution?
- Inline the existing keep-alive polling in both service worker entry
points.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: #43773

## **Manual testing steps**

1. Run `yarn start` and load the extension in Chrome.
2. Open `chrome://extensions`, click **Service worker** for MetaMask,
and run:
`let { timestamp } = await chrome.storage.session.get('timestamp');
console.log(timestamp)`
3. Verify a recent ISO timestamp is logged immediately after startup.
4. Reload the extension and confirm the timestamp continues updating
every ~2 seconds.
5. Unlock MetaMask and perform basic actions (open popup, switch
accounts) to confirm normal background behavior is unchanged.

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

### **Before**

### **After**
-->

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Behavior is unchanged aside from earlier polling start; no auth,
vault, or transaction logic is touched—only duplicated keep-alive code
in known MV3 entry files.
> 
> **Overview**
> **MV3 keep-alive** now starts at service worker module load instead of
waiting until `background.js` finishes loading persisted state.
> 
> The existing `saveTimestamp` + 2s `setInterval` that writes an ISO
`timestamp` to `chrome.storage.session` is **inlined** in both MV3 entry
points (`service-worker.ts` for webpack dev, `app-init.js` for
browserify prod/E2E) and **removed** from `background.js` /
`initialize()`.
> 
> That closes the early-startup window where the worker could idle
before the dynamic `background.js` import completed.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8a73c71. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

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

## **Description**

Renders the QR sync code as an `<img>` generated from a data URL instead
of the table-based `QRCode` component. This ensures the QR code displays
correctly on small viewports such as the side-panel view, where the
previous rendering did not scale reliably. The Skeleton loader now
matches the QR code's dimensions so there is no layout shift while the
payload loads.

## Changes

- **`qr-code-scan.tsx`**
- Replaced the `QRCodeImage` component with a QR data URL generated
directly via `qrcode-generator`, rendered as an `<img>` (340×340) so it
displays properly on small screens like the side panel.
- Memoized QR generation with `useMemo` keyed on the displayed payload
to avoid regenerating on every render.
- Sized the `Skeleton` loader to the same 340×340 dimensions as the QR
code to prevent layout shift between the loading and loaded states.
- Added a centered MetaMask fox logo overlay on a fixed white background
(theme-independent) over the QR code.
- **`qr-code-scan.test.tsx`**
- Updated mocks to stub `qrcode-generator` (returning a mock data URL)
instead of mocking the removed `QRCodeImage` component.

## Motivation

The table-based `QRCode` did not render/scale reliably in the
constrained side-panel viewport. Using an image sourced from a generated
data URL renders consistently across screen sizes.


<!--
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: display 1:1 proportion of qr code for smaller screen

## **Related issues**

Fixes:

## **Manual testing steps**

1. Open MetaMask extension on side panel mode
2. Go to Menu > Settings > Sync with mobile
3. Check QR code image 

## **Screenshots/Recordings**

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

### **Before**
<img width="357" height="694" alt="Screenshot 2026-07-14 at 1 01 28 PM"
src="https://github.com/user-attachments/assets/0cf23a6b-551e-4d34-b494-01bd302c6b6a"
/>


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

### **After**
<img width="355" height="577" alt="Screenshot 2026-07-14 at 1 08 29 PM"
src="https://github.com/user-attachments/assets/84b8a8a7-6284-49a6-919d-5474ba3549a6"
/>


<!-- [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**
> Localized UI change on the sync-accounts QR screen with no auth or
persistence impact; deeplink QR still uses the existing table-based
component.
> 
> **Overview**
> **Sync-with-mobile QR** no longer uses the shared `QRCodeImage` table
renderer; it builds a **data URL** with `qrcode-generator`
(`createDataURL` instead of `createTableTag`) and shows a fixed
**340×340** `<img>` so the code scales reliably in the side panel.
> 
> QR generation is **memoized** on the displayed payload, the loading
**skeleton** matches the same size to avoid layout shift, and a
**centered MetaMask fox** sits on a theme-independent white badge over
the code. Tests now mock **`qrcode-generator`** instead of
`QRCodeImage`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bdfe05e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

After a local EVM transaction confirms, the activity list can show local
state until the API query cache is refreshed after a stale time window.

This PR invalidates the query cache on confirmation

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Open the extension and ensure you have at least two accounts.
2. From Account A, send a small amount of native ETH to Account B.
3. While the tx is pending, open the Activity tab — confirm the pending
row appears.
4. Wait for the tx to confirm.
5. Open the confirmed tx details — verify the **Network Fee** row is
present immediately after confirmation (previously it would be missing
until the next stale refetch or window focus).
6. Optionally check an older confirmed tx to confirm the Network Fee row
is still present there too.

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

### **Before**

### **After**
-->

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

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

Tiny PR to add the filled candlestick version icon for Perps on the
bottom nav when the tab is active. Behind feature flag.

active
<img width="443" height="72" alt="image"
src="https://github.com/user-attachments/assets/3080a292-929b-463b-9c5b-af4c7f3df499"
/>

inactive
<img width="440" height="75" alt="image"
src="https://github.com/user-attachments/assets/8d42fac9-76ac-40bb-92a9-a1df2352b528"
/>


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

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.
)

<!--
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?
-->
```markdown
## [1.31.0]

### Changed

- Use a `37 TRX` fallback fee when the network fee cannot be estimated for a transaction ([#344](MetaMask/snap-tron-wallet#344))

## [1.30.0]

### Changed

- Increase cronjob interval for account syncing from 30s to 60s ([#356](MetaMask/snap-tron-wallet#356))
```

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

Related to: https://consensyssoftware.atlassian.net/browse/WPN-1598

## **Manual testing steps**

1. Open **offscreen.html** devtools, switch to the network tab
2. Filter by "trongrid"
3. Observe calls firing every 60 seconds 

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [ ] 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**
> No extension source changes; only a version pin update with a
behavioral tweak inside the snap (less frequent sync), which is low risk
for core wallet flows.
> 
> **Overview**
> Bumps the preinstalled **`@metamask/tron-wallet-snap`** dependency
from **`^1.29.1`** to **`^1.31.0`** in `package.json` and refreshes
**`yarn.lock`** so the extension ships the newer snap build.
> 
> Per the linked snap release notes, this line includes slowing the
**account-sync cron** from **30s to **60s**, which should cut
**TronGrid** polling frequency in the offscreen snap context.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e5bd2c1. 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**

Migrates the mUSD claim toast from the custom `MerklClaimToast`
component to the app-wide toast listener.

This prevents overlapping toasts and keeps this mUSD toast UI consistent
with other transaction toasts.

## **Changelog**

CHANGELOG entry: null

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

Fixes:
-->

## **Manual testing steps**

1. From the Tokens list, claim an mUSD bonus
2. Confirm the pending toast shows "Claiming rewards..."
3. Confirm the success toast shows "Rewards claimed!" after confirmation

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

### **Before**

### **After**
-->

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

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

QrSync (Settings → Sync accounts) depends on a mobile wallet scanning a
QR code and exchanging messages over the Mobile Wallet Protocol (MWP)
relay. That makes the happy path difficult to cover in extension E2E
tests without a real mobile client or relay.

This PR adds an **in-extension MWP mock stack** for test builds and
wires it into the existing E2E harness:

- **`mwp-dapp-client-factory`** selects the production MWP stack
(`WebSocketTransport` + `SessionStore`) in normal builds, and an
**`E2eMwpMockClient`** + **`MobileWalletSimulator`** when `IN_TEST` is
set and Jest is not running.
- **`qr-sync-e2e-bridge`** registers the simulator and handles a new
background-socket command, **`qrSyncSimulate`**, so Mocha tests can
drive mobile-side events (scan → OTP → sync offer → sync
completed/cancel/error).
- **`QrSyncController`** now initializes MWP through `getMwpDappClient`
instead of wiring transport/session store inline.
- **Test ergonomics:** shorter `QR_SYNC_TIMEOUT_MS` values in test
builds; test builds force `ADD_DEVICE_SYNC_ENABLED` so the Sync accounts
entry is available in E2E.
- **E2E coverage:** page objects, `data-testid` hooks on sync-accounts
UI steps, and a happy-path spec (`syncs a single HD wallet to mobile`).
- **Unit tests** for `E2eMwpMockClient` and `MobileWalletSimulator`.

Production behavior is unchanged outside extension test builds.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

1. Build and load a **non-test** dev extension (`yarn start`), unlock
the wallet, and confirm the extension starts without QrSync/MWP
initialization errors in the background console.
2. Build and load a **test** extension (`yarn build:test`), unlock the
wallet, open **Settings → Sync accounts**, and confirm the QR code
screen renders and the step UI (OTP, password, wallet selection,
loading, success) displays correctly.
3. With the test build loaded, walk through **Settings → Sync accounts**
and confirm each step transitions as expected when mobile events are
simulated (OTP entry with `123456`, password entry, wallet selection,
sync confirmation).

<!--
## **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**
> Refactors QrSync MWP initialization on a wallet-export path;
production stack is preserved but any factory regression could break
real relay pairing, while the mock stack only activates in extension
test builds.
> 
> **Overview**
> Adds **extension E2E coverage** for Settings → Sync accounts by
introducing a **test-build MWP mock** and wiring it through the existing
background-socket harness, while **refactoring** how `QrSyncController`
boots the Mobile Wallet Protocol client.
> 
> **`mwp-dapp-client-factory`** centralizes MWP setup: normal builds
still use `WebSocketTransport` + `SessionStore` + `DappClient`, but when
`IN_TEST` is set **and Jest is not running**, it loads an
**`E2eMwpMockClient`** and **`MobileWalletSimulator`** (via dynamic
`require` so test helpers stay out of production bundles).
`QrSyncController` now calls **`getMwpDappClient`** instead of owning
transport/session fields inline.
> 
> E2E tests drive the mobile side with a new **`qrSyncSimulate`**
background-socket command handled by **`qr-sync-e2e-bridge`** (scan →
OTP → sync offer → completed/cancel/error). **`QR_SYNC_TIMEOUT_MS`**
uses shorter values under `IN_TEST` so flows finish quickly.
> 
> The compile-time flag is renamed **`ADD_DEVICE_SYNC_ENABLED` →
`QR_SYNC_ENABLED`** (`.metamaskrc`, `builds.yml`, `environment.ts`,
docs). **Test builds force `QR_SYNC_ENABLED=true`** so the Sync accounts
tab is available in E2E.
> 
> Supporting changes: **`data-testid`** hooks across sync-accounts UI
steps, settings/sync page objects, a happy-path **`qr-sync.spec.ts`**,
and unit tests for the factory (Jest still uses the production stack
mocks).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0a84dab. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: lwin <lwin.kyaw@consensys.net>
Co-authored-by: Ganesh Suresh Patra <ganesh.patra@consensys.net>
Co-authored-by: Lionell Briones <llenoil@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
…o metamask-ci (#44387)

## **Description**

The GitHub App `mm-token-exchange-service` (App ID `3202397`) is being
renamed to `metamask-ci` in the MetaMask org. On rename, its bot login
changes from `mm-token-exchange-service[bot]` to `metamask-ci[bot]`.
This PR updates the one place in this repo that hardcodes the old bot
slug:

- `.github/workflows/cla.yml`: CLA `allowlist:` entry
`mm-token-exchange-service[bot]` → `metamask-ci[bot]` (no other entries
changed).

**⚠️ Merge in lockstep with the app rename — merge this immediately
*after* TechOps renames the app, not before.** Tracked in INFRA-3680 /
INFRA-3764. This ordering is deliberate and security-relevant: merging
at/after the rename means `metamask-ci[bot]` is trusted only once we
actually own that name, and the old `mm-token-exchange-service[bot]` is
dropped the instant its name is freed — so there is no window where an
unowned/freed GitHub App name is trusted by the CLA allowlist. This PR
is kept review-ready so the merge is a single click during the
coordinated cutover window.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Refs: https://consensyssoftware.atlassian.net/browse/INFRA-3680
Refs: https://consensyssoftware.atlassian.net/browse/INFRA-3763

## **Manual testing steps**

1. After TechOps renames the app to `metamask-ci` and this merges, have
`metamask-ci[bot]` open a PR / post an automated comment.
2. Confirm the CLA check auto-approves it via the updated allowlist (no
CLA failure on the bot's contribution).

Note: CI/config-only change; rename behavior validated in a
`Consensys-test` dry-run (bot login updates in place, App ID unchanged)
— see INFRA-3762.

## **Screenshots/Recordings**

N/A — CI workflow configuration change; no extension UI or runtime
behavior is affected.

### **Before**

N/A

### **After**

N/A

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

Copy link
Copy Markdown
Contributor

@metamaskbot update-policies

@metamask-ci

metamask-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

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

Tip

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

👀 lavamoat/webpack/mv2/beta/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv2/experimental/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv2/flask/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv3/beta/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes
👀 lavamoat/webpack/mv3/experimental/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes
👀 lavamoat/webpack/mv3/flask/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes

@HowardBraham

Copy link
Copy Markdown
Contributor

@metamaskbot update-policies

@metamask-ci

metamask-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

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

Tip

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

👀 lavamoat/webpack/mv2/beta/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv2/experimental/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv2/flask/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes
👀 lavamoat/webpack/mv3/beta/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes
👀 lavamoat/webpack/mv3/experimental/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes
👀 lavamoat/webpack/mv3/flask/policy.json changes differ from lavamoat/webpack/mv3/main/policy.json changes

@HowardBraham

Copy link
Copy Markdown
Contributor

@SocketSecurity ignore npm/@metamask/solana-test-validator-up@1.0.0
@SocketSecurity ignore npm/@metamask/transaction-controller@69.0.0
@SocketSecurity ignore npm/@metamask/transaction-pay-controller@24.1.0
@SocketSecurity ignore npm/@parcel/watcher@2.5.6
@SocketSecurity ignore npm/content-type@2.0.0
@SocketSecurity ignore npm/js-yaml@3.15.0
@SocketSecurity ignore npm/node-addon-api@7.1.1
@SocketSecurity ignore npm/type-is@2.1.0

@metamask-ci

metamask-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
Builds ready [61a42a4]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 12 pass · 🟡 8 warn · 🔴 4 fail)

Baseline (latest main): 55ad54b | Date: 7/24/2026 | Pipeline: 30124316022 | Baseline logs

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

Regressions (🔴 4 failures)

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

📈 Results compared to the previous 5 runs on main

  • ↓ loadNewAccount/load_new_account: -75%
  • ↓ loadNewAccount/total: -75%
  • ↓ loadNewAccount/inp: -38%
  • ↑ loadNewAccount/fcp: +28%
  • ↓ confirmTx/longTaskTotalDuration: -31%
  • ↓ confirmTx/longTaskMaxDuration: -36%
  • ↓ confirmTx/tbt: -57%
  • ↓ confirmTx/inp: -49%
  • ↓ confirmTx/fcp: -31%
  • ↓ confirmTx/lcp: -23%
  • ↓ bridgeUserActions/bridge_load_page: -31%
  • ↓ bridgeUserActions/bridge_load_asset_picker: -39%
  • ↓ bridgeUserActions/longTaskCount: -44%
  • ↓ bridgeUserActions/longTaskTotalDuration: -56%
  • ↓ bridgeUserActions/longTaskMaxDuration: -34%
  • ↓ bridgeUserActions/tbt: -80%
  • ↓ bridgeUserActions/total: -15%
  • ↓ bridgeUserActions/inp: -42%
  • ↓ bridgeUserActions/fcp: -32%
  • ↓ bridgeUserActions/lcp: -27%
  • ↑ loadNewAccount/load_new_account: +117%
  • ↑ loadNewAccount/total: +117%
  • ↑ loadNewAccount/lcp: +1189%
  • ↓ confirmTx/longTaskCount: -100%
  • ↓ confirmTx/longTaskTotalDuration: -100%
  • ↓ confirmTx/longTaskMaxDuration: -100%
  • ↓ confirmTx/tbt: -100%
  • ↓ confirmTx/inp: -35%
  • ↑ confirmTx/fcp: +13%
  • ↑ confirmTx/lcp: +1243%
  • ↑ bridgeUserActions/bridge_load_page: +208%
  • ↑ bridgeUserActions/bridge_load_asset_picker: +1186%
  • ↓ bridgeUserActions/longTaskCount: -100%
  • ↓ bridgeUserActions/longTaskTotalDuration: -100%
  • ↓ bridgeUserActions/longTaskMaxDuration: -100%
  • ↓ bridgeUserActions/tbt: -100%
  • ↑ bridgeUserActions/total: +196%
  • ↓ bridgeUserActions/inp: -28%
  • ↑ bridgeUserActions/fcp: +12%
  • ↑ bridgeUserActions/lcp: +1207%

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

  • 🟡 loadNewAccount/FCP: p75 2.2s
  • 🟡 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]
startupPowerUserHome
[Sentry log · main/release]
–🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↑ startupStandardHome/setupStore: +21%
  • ↓ startupStandardHome/lcp: -37%
  • ↑ startupStandardHome/domInteractive: +64%
  • ↑ startupStandardHome/initialActions: +11%
  • ↑ startupStandardHome/fcp: +71%
  • ↑ startupPowerUserHome/setupStore: +46%
  • ↑ startupPowerUserHome/inp: +13%

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

  • 🟡 startupPowerUserHome/INP: p75 240ms
  • 🟡 startupPowerUserHome/LCP: p75 3.4s
User Journey Benchmarks · Samples: 5 · real API 🔴 3
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🔴 [CI log]
🔴 doneButtonToHomeScreen
🔴 total
🔴 [CI log]
🔴 total
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 total
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • ↓ onboardingImportWallet/metricsToWalletReadyScreen: -19%
  • ↓ onboardingImportWallet/doneButtonToHomeScreen: -45%
  • ↓ onboardingImportWallet/openAccountMenuToAccountListLoaded: -91%
  • ↓ onboardingImportWallet/total: -51%
  • ↓ onboardingNewWallet/skipBackupToMetricsScreen: -16%
  • ↓ onboardingNewWallet/doneButtonToAssetList: -65%
  • ↑ onboardingNewWallet/longTaskCount: +25%
  • ↑ onboardingNewWallet/longTaskTotalDuration: +29%
  • ↑ onboardingNewWallet/longTaskMaxDuration: +30%
  • ↑ onboardingNewWallet/tbt: +75%
  • ↓ onboardingNewWallet/total: -62%
  • ↓ solanaAssetDetails/longTaskCount: -100%
  • ↓ solanaAssetDetails/longTaskTotalDuration: -100%
  • ↓ solanaAssetDetails/longTaskMaxDuration: -100%
  • ↓ solanaAssetDetails/tbt: -100%
  • ↓ solanaAssetDetails/inp: -26%
  • ↑ solanaAssetDetails/cls: +627%
  • ↓ importSrpHome/openAccountMenuAfterLogin: -96%
  • ↓ importSrpHome/homeAfterImportWithNewWallet: -67%
  • ↓ importSrpHome/longTaskCount: -24%
  • ↓ importSrpHome/longTaskTotalDuration: -23%
  • ↓ importSrpHome/longTaskMaxDuration: -25%
  • ↓ importSrpHome/tbt: -23%
  • ↓ importSrpHome/total: -64%
  • ↓ importSrpHome/cls: -53%
  • ↑ sendTransactions/openSendPageFromHome: +14%
  • ↑ sendTransactions/longTaskCount: +67%
  • ↑ sendTransactions/longTaskTotalDuration: +54%
  • ↑ sendTransactions/longTaskMaxDuration: +54%
  • ↓ sendTransactions/tbt: -11%
  • ↑ sendTransactions/cls: +506%
  • ↓ swap/longTaskCount: -17%
  • ↑ swap/longTaskTotalDuration: +11%
  • ↑ swap/longTaskMaxDuration: +18%
  • ↑ swap/tbt: +50%
  • ↓ swap/inp: -32%
  • ↓ swap/cls: -13%

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

  • 🟡 importSrpHome/INP: p75 320ms
  • 🟡 solanaAssetDetails/FCP: p75 2.0s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 2.0s
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: +10%
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 10.42 KiB (0.07%)
  • ui: 13.53 KiB (0.08%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 882 Bytes (0.04%)
  • zip: 5.53 KiB (0.02%)

🍒 What's in this RC

Cherry-picks (15 commits)
Commit Description
61a42a48c7 Update attributions
49f7a6196b Update LavaMoat policies
8f00f2c8a1 release(cp): bump: tar to 7.5.22, ignore react-router advisories cp-13.41.0 (#44862)
59a4a4a89f release: ignore postcss vulnerabilities temporarily for release 13.41.0
6b30cb2d03 release(cp): bump: valibot to 1.4.2, body-parser to 1.20.6 and 2.3.0 (#44664)
40376af31f release(runway): cherry-pick fix: restored old behavior to show Paid by MetaMask label in sponsored transactions from activity page cp-13.41.0 (#44843)
2ff9612f4e release(runway): cherry-pick fix(assets): include tokens with large balances and few decimals in aggregated balance cp-13.41.0 (#44808)
2c0238f697 fix: patch bridge controller to exclude Stellar and Arc (#… (#44787)
798a7b4fec release(runway): cherry-pick fix: updated search to be sticky and banner to scroll cp-13.41.0 (#44770)
ffbf714505 release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action cp-13.41.0 (#44735)
b80b3fb71c release(runway): cherry-pick bump: immutable to 5.1.9, fast-uri to 3.1.4, dompurify to 3.4.12, sass-embedded to 1.100.0 cp-13.41.0 (#44719)
9428de0346 release(runway): cherry-pick fix(ci): align Extension RC Slack notes with Mobile Runway changelog cp-13.41.0 (#44723)
34ca743df7 release(runway): cherry-pick bump: multiple to fix audit (#44643)
1951884be3 release(runway): cherry-pick fix: dedicated convert mUSD details cp-13.41.0 (#44613)
1cc5bbb0d4 Merge branch 'stable' into release/13.41.0

Changelog (123 commits since v13.40.0)
Commit Description
61a42a48c7 Update attributions
49f7a6196b Update LavaMoat policies
8f00f2c8a1 release(cp): bump: tar to 7.5.22, ignore react-router advisories cp-13.41.0 (#44862)
59a4a4a89f release: ignore postcss vulnerabilities temporarily for release 13.41.0
6b30cb2d03 release(cp): bump: valibot to 1.4.2, body-parser to 1.20.6 and 2.3.0 (#44664)
40376af31f release(runway): cherry-pick fix: restored old behavior to show Paid by MetaMask label in sponsored transactions from activity page cp-13.41.0 (#44843)
2ff9612f4e release(runway): cherry-pick fix(assets): include tokens with large balances and few decimals in aggregated balance cp-13.41.0 (#44808)
2c0238f697 fix: patch bridge controller to exclude Stellar and Arc (#… (#44787)
798a7b4fec release(runway): cherry-pick fix: updated search to be sticky and banner to scroll cp-13.41.0 (#44770)
ffbf714505 release(runway): cherry-pick chore: New Crowdin Translations by GitHub Action cp-13.41.0 (#44735)
b80b3fb71c release(runway): cherry-pick bump: immutable to 5.1.9, fast-uri to 3.1.4, dompurify to 3.4.12, sass-embedded to 1.100.0 cp-13.41.0 (#44719)
9428de0346 release(runway): cherry-pick fix(ci): align Extension RC Slack notes with Mobile Runway changelog cp-13.41.0 (#44723)
34ca743df7 release(runway): cherry-pick bump: multiple to fix audit (#44643)
1951884be3 release(runway): cherry-pick fix: dedicated convert mUSD details cp-13.41.0 (#44613)
7ea01fa18e release(runway): cherry-pick feat: added decimal validation for custom token import flow cp-13.41.0 (#44604)
ec4acd3e92 release: release-changelog/13.41.0 (#44584)
3b6d3ebc7f release: update attributions
f332f33617 release: fix the automatic "Merge release/13.40.0 into release/13.41.0"
6db181c2a2 Merge release/13.40.0 into release/13.41.0
5d12649353 test: fix flakyTest Snap getEntropy can use snap_getEntropy inside a snap (#44458)
ad3fb497ce refactor(confirmations): address send asset picker review follow-ups (#44431)
b6449df15d feat(ramps): update remaining entrypoints to use goToBuy (#44440)
8a1ccdc82c feat: gate scam questionnaire behind LaunchDarkly flag cp-13.40.0 (#44496)
c0e7a119d7 fix: mascot being rendered twice in onboarding unlock page (#44533)
4c7819aff3 fix: prevent duplicate events during tab switch (#44528)
360bc7d616 fix: bump network enablement controller to v5.6.0 (#44371)
e2f4454072 feat(ramps): add payment method selection page (#44437)
d11fa4a85a refactor: migrate gas fee token toast (#44468)
b48258de3b feat(e2e): add Tron local node mock proxy (#44157)
e149e9c519 feat: update bridge status controller to latest release (#44515)
2ba671463a bump: websocket-driver to fix audit (#44513)
8b843741f8 fix(ramps): wait for selected token before leaving token selection (#44497)
449919f484 fix: crash when typing a comma in the MM Pay custom amount input (#44521)
81ed3e6cb7 fix(ramps): keep provider label while quotes load on build quote (#44491)
e294e5fbc3 chore(tokens): remove stale token cache fallbacks (#44522)
b3a729e909 build: remove submodule added by mistake (#44514)
40f4844d3e feat: sentry for QrSync (#44487)
173e9e7a04 feat: added transitions to manage tokens page (#44484)
be631b56fa refactor: switch activity mappers to @metamask/client-utils (#44366)
41ca3b93c3 fix: Bump react-data-query (#44520)
0cd688b4bf refactor(analytics): migrate orphan multichain accounts UI events (#44376)
4400dd58c1 feat: added skeleton fr balance loading state (#44429)
397c4485f0 chore: use skeleton on loading activity screen (#44423)
29b6f2e924 chore: Revert "chore(6926): migrate ReactDOM.render to createRoot (#43872)" (#44517)
743cccdd2a refactor(analytics): migrate orphan account overview tabs events (#44375)
9efc6d62ed chore(6926): migrate ReactDOM.render to createRoot (#43872)
83d763aaa4 perf(6600): fix asset selector cache thrashing for NFTs and token scan results (#44473)
21c3992646 feat: added transitions to Dapp connections pages (#44481)
1d44802063 fix: consume local history data for bridges on activity items (#44488)
bf71e30756 refactor(analytics): migrate orphan UX contacts and chrome events (#44374)
f1c3e4103c feat(e2e): extend Tron mocks with swap tokens, stateful accounts, and parameterized fixtures (#44485)
8c0fa5b830 perf(6601): fix parameterized selector cache thrashing for chain-checking selectors (#44474)
c255a1a983 feat(perps): show ticker next to volume and fix symbol display consistency (#44478)
b169cbc8fc fix: updated analytis for add token (#44486)
d0a5db3726 feat(ramps): add build quote page with quote fetching (#44409)
4eabcbe89b chore(6931): migrate class components from defaultProps to default params (#44299)
56e0cbf45f perf(6929): harden UI effects for React 18 StrictMode double-mount (#44298)
55e0419dce perf(6602): prevent cache thrashing in parameterized network lookups (#44475)
72c5dccaa4 chore(6930): update React Compiler target to v18 (#44476)
9b7cd98c49 feat: updated CODEOWNERS for QrSync (#44465)
f2018259e3 test: QrSync e2e for multi SRP flows (#44438)
dc5bc8dc93 test(e2e): add Bitcoin send flow against local regtest node (#44156)
e34d7e67bb test: Add E2E max balance validation test (#43821)
2ff51fd902 fix: ensure stellar assets show correctly in token details page (#44444)
77cf93f811 chore: tiny header adjustments on new bottom nav pages (#44470)
fe2bc9df7d test: MMQA - 1971 - Fix missnamed test tokens/nft/filter-nfts.spec.ts same as view-nft-details.spec.ts (View NFT details) (#44086)
f5d4634e8b perf(7475): adopt useDeferredValue for search and filter surfaces (#44443)
23a9a0e228 feat(e2e): add Bitcoin regtest node wrapper using @metamask/bitcoin-regtest-up (#44155)
1986666442 feat: updated token management toggle button (#44434)
a74972d532 feat: ux improvemnets for add via chainlist feature (#44424)
bfb0b29226 test: fix flaky test Vault Corruption does not reset metamask state when recovery is not confirmed (#44435)
ccadeeeccd chore: New Crowdin Translations by GitHub Action cp-13.40.0 (#44329)
3f7584b6f0 fix: QR Sync session timeout and cancellation (#44422)
8b02f4a613 chore: reduce Sentry trace sampling cp-13.40.0 (#44451)
7cd63e4d54 perf(6778): add useStateSyncHealth hook for stale sync auto-recovery (#44389)
80a76d63d4 bump: ws to fix audit (#44459)
f602fae822 refactor: migrate perps withdraw transaction toast (#44419)
81bacd28ee fix(sentry): Resolve AggregatedBalanceSelector transaction volume spike by not passing trace into getAggregatedBalanceForAccount cp-13.40.0 (#44449)
3d015e0071 Merge origin/main into stable-main-13.39.2
ba18ce10ac feat(ramps): intent routing in goToBuy + stub buy pages (#44404)
2e477542ab ci(token-exchange): migrate FIXTURE_UPDATE_TOKEN to token exchange service (#43838)
2ff80c450e test(e2e): run Solana send flow against local validator (#44154)
baab980461 fix: resolve Perps deposit confirmation stuck on loading skeleton on first open (#44247)
a9f826964b ci: remove requirement for "auto-rc-builds" label (#44421)
3e52ec5ffb perf(7467): memoize composite derived state in home container and dapp bar (#44354)
8846ae56e5 feat(ramps): geo-blocking UI for ramps entry points (#44351)
c496204040 feat(e2e): add Solana local validator wrapper using @metamask/solana-… (#44426)
71f9467411 feat: reintroduce saved gas settings (#43317)
25d7175ff4 chore: nav to perps funding screen on perps funded activity details (#44427)
7657dcc0da fix: render in flight perps deposit/withdraw activity details (#44425)
78908aa279 fix(confirmations): address enforced simulations papercuts (#44343)
4b01eb4a84 feat(ramps): add in-extension token selection page (#44349)
eb3b6f521a fix(perps): mask open order size and value in privacy mode (#44432)
c6c99ecced ci(INFRA-3680): rename bot reference from mm-token-exchange-service to metamask-ci (#44387)
594bbd6f6c test: QrSync E2E (main flow) (#44381)
612334352b refactor: migrate musd claim toast (#44414)
d22a7b6118 chore: bump @metamask/tron-wallet-snap to ^1.31.0 cp-13.40.0 (#44385)
a396544397 chore: use candlestick filled icon when perps bottom nav active (#44388)
70a302b923 feat: refresh activity list after confirm (#44333)
ea74194fd2 feat: sync-accounts use image for qr code (#44417)
4631b6103f fix(mv3): move keep-alive polling to service worker startup (#44348)
5dd78ebf23 refactor(analytics): migrate settings thunk MetaMetrics events (#44379)
b400e49380 refactor(analytics): migrate orphan platform metrics UI events (#44378)
1e0188a092 perf(7465): memoize token list derivations and lift per-row Redux subscriptions to parent (#44295)
e9d9c6cd75 fix: fixed race condition on vault creation and get seedphrase (#44276)
5418dedc88 chore(preferences): remove enableMV3TimestampSave debug preference (#44373)
5bfa8fe8fb chore(6925): upgrade react & react-dom to v18 (#42997)
d3fa3dd51b chore: update CODEOWNERS bringing more code under team money-movement (#44408)
485109f500 chore: deprecated import path (#43242)
dec283a8fe refactor(analytics): migrate orphan Web3Auth onboarding events (#44377)
5291e67aa5 refactor: remove headless STX status page approval (#44301)
909c8d60b2 docs: Add Cursor Skill to automatically add new EVM networks to swaps (#44386)
4a4948b41b fix: update trackImportEvent usage to use correct params and string comparison (#44400)
355bc49105 feat: Bump Snaps packages (#44396)
cd8907a7aa perf(7467): memoize hooks/components with multiple selectors (#44297)
7c8966dd69 feat(e2e): wire Tron local node into fixtures (#44152)
82af0c7e26 fix(assets): restore google.svg and relocate to app/images/ cp-13.40.0 (#44383)
b977317023 feat(asset): migrate asset routes to CAIP-19 identifiers (#44114)
0ebadf7dd6 feat: QR sync error UI (#44081)
615c3a8fe1 feat: rename add-device to sync-accounts (#43870)
a329b0473b fix: extra pending row from local state cp-13.40.0 (#44359)
b7c7eef85a feat: wallet metadata and imported account sync via QR (#44047)
b426596c9c perf: add useMemo/useCallback memoization to home balance components (#44294)

AI Test Plan

Risk Score High Risk Medium Risk Files Changed Commits
55/100 6 6 703 126
Cherry-Pick Scenarios (4)

High Risk Scenarios (2)

1. Token Management – Custom Token Import Decimal Validation

Risk Level: HIGH

Why This Matters: Cherry-pick #44604 fixes unsafe decimal inputs that can corrupt balances/amounts; validating boundaries prevents users from importing broken tokens.

Test Steps:

  1. Open Import Tokens > Custom; enter a valid ERC-20 address and manually input decimals outside valid bounds (e.g., negative, non-integer, or excessively large like 256). Verify a validation error and the import button is disabled.
  2. Input valid integer decimals (e.g., 0, 18) and confirm the import succeeds; verify the token balance displays with correct precision.
  3. Attempt to modify decimals post-detection (if editable) and ensure invalid entries are blocked with clear messaging.

2. Portfolio Totals – Include Tokens with Large Balances and Few Decimals

Risk Level: HIGH

Why This Matters: Cherry-pick #44808 fixes omission of high-value, low-decimal tokens from totals; inaccuracies here directly misrepresent user wealth.

Test Steps:

  1. Ensure an account holds a token with few decimals (e.g., 0–2) and a large balance; open All Accounts view and note the portfolio total.
  2. Toggle the network filter to include/exclude the token’s network and verify the aggregated total updates to include the token correctly.
  3. Compare the UI total to a manual sum of included assets to confirm no rounding or exclusion errors.

Medium Risk Scenarios (2)

1. Activity – Sponsored Transactions Label ('Paid by MetaMask')

Risk Level: MEDIUM

Why This Matters: Cherry-pick #44843 restores critical labeling for sponsored txs; without it, users may misunderstand who paid fees.

Test Steps:

  1. Perform or import a sponsored transaction (gas paid by MetaMask or a sponsor) and open the Activity tab for that account.
  2. Verify the transaction row and details view display the 'Paid by MetaMask' label; confirm normal transactions do not show this label.
  3. Open the transaction details panel and ensure the label persists consistently across views.

2. Convert – mUSD Dedicated Details

Risk Level: MEDIUM

Why This Matters: Cherry-pick #44613 fixes token-specific details for mUSD in Convert; incorrect details can cause user confusion or mispricing.

Test Steps:

  1. Open the Convert flow and select mUSD as source or destination (where available); review the quote details.
  2. Verify token name/icon, rate, fees, and network are accurately shown and consistent across quote and review screens.
  3. Change amount and re-quote; confirm details update correctly and no generic/incorrect labels appear.

Release Scenarios (8)

High Risk Scenarios (4)

1. State Migrations (217, 218)

Risk Level: HIGH

Why This Matters: Migrations can corrupt or drop user data (accounts, tokens, networks). Ensuring data integrity and normal operation post-upgrade is critical to avoid fund access issues.

Test Steps:

  1. Start from a 13.40 profile with: 3+ accounts (imported + hardware + watch-only), multiple custom networks (incl. testnets), and a diverse token list (custom and detected) with prior transaction history.
  2. Upgrade to 13.41.0 and open the extension to trigger migrations; wait until the UI is responsive.
  3. Verify all accounts, networks, and custom RPC settings persist; spot-check 3-5 tokens (incl. a custom token) for correct symbols/decimals/balances; confirm activity history remains.
  4. Lock and unlock the wallet; then add a new account and switch networks to ensure post-migration state behaves normally.

2. QR Sync (Pairing with Mobile) – Lifecycle

Risk Level: HIGH

Why This Matters: Pairing touches account exposure and secure communication between devices; regressions could lead to broken sync, stale sessions, or privacy leaks.

Test Steps:

  1. Go to Settings > Sync with Mobile and generate a QR; scan from MetaMask Mobile to pair and approve any prompts on both sides.
  2. After pairing, verify the expected accounts and network selection synchronize correctly (e.g., account names, selected account).
  3. Lock and unlock the extension; ensure the pairing remains active and account selection stays in sync.
  4. Disconnect/revoke pairing from the extension; confirm mobile shows the session as terminated and the extension no longer lists the paired device.

3. Dapp Network Management (wallet_addEthereumChain / wallet_switchEthereumChain)

Risk Level: HIGH

Why This Matters: Recent controller and dependency changes can affect add/switch flows; misconfigurations may expose users to incorrect networks or failed dapp interactions.

Test Steps:

  1. From a test dapp, trigger wallet_addEthereumChain for a new chain (valid chainId, RPC URL, block explorer). Approve in the extension and confirm the network is added and becomes active.
  2. Trigger wallet_switchEthereumChain to an existing chain; approve and verify the active network changes and subsequent dapp calls use the new chain.
  3. Attempt adding a chain with mismatched chainId/RPC or an invalid RPC; reject and verify no partial/invalid network entry is created.
  4. Reject a valid add/switch request and confirm the previous network remains active and no stray confirmations persist.

4. Assets List – Network Filter and Totals

Risk Level: HIGH

Why This Matters: Large UI changes to filtering increase the risk of incorrect balances, hidden tokens, or confusing totals that could mislead users about their holdings.

Test Steps:

  1. On the Assets tab with multiple networks enabled (e.g., Mainnet, Arbitrum, Sepolia), open the network filter and select a subset of networks.
  2. Verify the token list updates to only show assets from selected networks and the total asset value reflects the filtered set.
  3. Use the search field to find a token, then change the network filter; confirm results remain consistent with the new filter.
  4. Clear filters and confirm the full list and totals return without duplicates or missing entries.

Medium Risk Scenarios (4)

1. Account Group Balance (All Accounts view) – Aggregation Accuracy

Risk Level: MEDIUM

Why This Matters: Incorrect aggregation can overstate or understate a user’s portfolio, leading to poor financial decisions.

Test Steps:

  1. Enable All Accounts view and ensure at least two accounts hold overlapping and unique tokens across different networks.
  2. Verify the aggregated balance matches the manual sum of the visible tokens from all accounts for the selected networks.
  3. Hide/unhide a token in one account and confirm the aggregated total updates accurately without impacting other accounts’ totals.
  4. Switch to a different set of networks in the filter and confirm the aggregated balance recalculates correctly.

2. Asset Picker – Consistency Across Flows (Send/Swap/Bridge)

Risk Level: MEDIUM

Why This Matters: Shared token selection UI can desync across flows after refactors, causing user confusion or wrong-asset selection.

Test Steps:

  1. Initiate Send, then open the asset picker; verify listed tokens respect hidden-token settings and current network filter.
  2. Switch to Swap or Bridge flows and open the asset picker; confirm the same tokens and ordering/labels appear consistently.
  3. Search within the asset picker and select a token; ensure the selection propagates correctly to the flow and balances/decimals display as expected.
  4. Change networks and reopen the picker; verify it refreshes to the network’s token set without stale entries.

3. Bridge Quotes and Review (Bridge Controller patch)

Risk Level: MEDIUM

Why This Matters: Controller patching can break quoting or review logic; users rely on accurate routes and fees for safe cross-chain transfers.

Test Steps:

  1. Open Bridge, select a token and source/destination networks; request quotes and verify they load with expected providers and fee details.
  2. Select a quote to open the review screen; verify token amounts, slippage, and fees are accurate and the CTA is enabled/disabled appropriately.
  3. Trigger an error condition (e.g., unsupported route or insufficient liquidity) and verify meaningful error messaging and recovery (retry/change route).
  4. Cancel the review and confirm no partial approvals/transactions were created.

4. Unconnected Account Alert – Correct Visibility

Risk Level: MEDIUM

Why This Matters: Incorrect alerts can cause missed transactions or confusion about which account is interacting with a site.

Test Steps:

  1. Connect a dapp to Account A; switch the extension to Account B and visit the connected dapp.
  2. Verify the Unconnected Account alert appears and provides a clear path to switch to Account A.
  3. Switch back to Account A and reload the dapp; confirm the alert no longer shows.
  4. Disconnect the site and confirm the alert does not show for either account.

Teams Sign-off Status

Signed off: None yet

Awaiting sign-off (7):
Accounts, Assets, Networks, Swaps, Swaps and Bridge, Transactions, Wallet Integrations


Generated by AI Test Plan Analyzer (gpt-5) at 2026-07-24T21:03:54.522Z

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

This branch was previously deployed

3 inactive deployments
release-branch — 61a42a48 Deployed Jul 24, 2026 by HowardBraham via Publish release #42
pr-comment — 61a42a48 Deployed Jul 24, 2026 by HowardBraham via Publish prerelease / Publish prerelease #154518
release-ci — 61a42a48 Deployed Jul 24, 2026 by HowardBraham via Publish prerelease / Generate AI test plan #154518
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.41.0 Issue or pull request that will be included in release 13.41.0 skip-benchmark-gate Disables `run-benchmarks/quality-gate` job team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.