release: 13.41.0 - #44583
release: 13.41.0#44583
Conversation
…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.
|
@metamaskbot update-policies |
|
Policies updated. Tip Follow the policy review process outlined in the LavaMoat Policy Review Process doc before expecting an approval from Policy Reviewers. 👀 lavamoat/webpack/mv2/beta/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes |
|
@metamaskbot update-policies |
|
Policies updated. Tip Follow the policy review process outlined in the LavaMoat Policy Review Process doc before expecting an approval from Policy Reviewers. 👀 lavamoat/webpack/mv2/beta/policy.json changes differ from lavamoat/webpack/mv2/main/policy.json changes |
|
@SocketSecurity ignore npm/@metamask/solana-test-validator-up@1.0.0 |
Builds ready [61a42a4]
⚡ Performance Benchmarks (Total: 🟢 12 pass · 🟡 8 warn · 🔴 4 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
🍒 What's in this RCCherry-picks (15 commits)
Changelog (123 commits since v13.40.0)
AI Test Plan
Cherry-Pick Scenarios (4)High Risk Scenarios (2)1. Token Management – Custom Token Import Decimal ValidationRisk 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:
2. Portfolio Totals – Include Tokens with Large Balances and Few DecimalsRisk 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:
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:
2. Convert – mUSD Dedicated DetailsRisk 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:
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:
2. QR Sync (Pairing with Mobile) – LifecycleRisk 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:
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:
4. Assets List – Network Filter and TotalsRisk 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:
Medium Risk Scenarios (4)1. Account Group Balance (All Accounts view) – Aggregation AccuracyRisk Level: MEDIUM Why This Matters: Incorrect aggregation can overstate or understate a user’s portfolio, leading to poor financial decisions. Test Steps:
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:
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:
4. Unconnected Account Alert – Correct VisibilityRisk Level: MEDIUM Why This Matters: Incorrect alerts can cause missed transactions or confusion about which account is interacting with a site. Test Steps:
Teams Sign-off StatusSigned off: None yet Awaiting sign-off (7): 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 |
🚀 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
Conduct regression and exploratory testing for your functional areas, including automated and manual tests for critical workflows.
Focus on exploratory testing across the wallet, prioritize high-impact areas, and triage any Sentry errors found during testing.
Validate new functionalities and provide feedback to support release monitoring.
GitHub Signoff
Issue Resolution
Cherry-Picking Criteria
🗓️ Timeline and Milestones
✅ Signoff Checklist
Each team is responsible for signing off via GitHub. Use the checkbox below to track signoff completion:
Team sign-off checklist
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