Skip to content

release: 7.72.0#27990

Merged
chloeYue merged 205 commits intostablefrom
release/7.72.0
Apr 7, 2026
Merged

release: 7.72.0#27990
chloeYue merged 205 commits intostablefrom
release/7.72.0

Conversation

@metamaskbot
Copy link
Copy Markdown
Collaborator

@metamaskbot metamaskbot commented Mar 26, 2026

🚀 v7.72.0 Testing & Release Quality Process

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


📋 Key Processes

Testing Strategy

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

GitHub Signoff

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

Issue Resolution

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

Cherry-Picking Criteria

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

🗓️ Timeline and Milestones

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

✅ Signoff Checklist

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

Team sign-off checklist

  • Accounts
  • Assets
  • Bots Team
  • Card
  • Confirmations
  • Core Platform
  • Design System
  • Earn
  • Engagement
  • Mobile Platform
  • Mobile UX
  • Money Movement
  • Networks
  • Onboarding
  • Perps
  • Predict
  • Product Safety
  • Rewards
  • Social & AI
  • Swaps and Bridge
  • team-network-enablement
  • team-ramp
  • Transactions
  • Wallet Integrations

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

bfullam and others added 30 commits March 19, 2026 18:44
## **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?
-->

This PR adds an A/B test for the bridge token selector balance layout.

Control keeps the current presentation by showing fiat balance on the
top row and keeping the ticker in the token balance text. Treatment
moves the token balance to the top row, removes the duplicate ticker
from the token balance text, and keeps the top and bottom rows aligned
with the intended size and color hierarchy.

The PR also passes the active experiment through the bridge page-view
and submit analytics paths using `active_ab_tests` so the treatment can
be evaluated against downstream conversion metrics.

## **Changelog**

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

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

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

CHANGELOG entry: Added an experiment for the bridge token selector
balance layout.

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Bridge token selector balance layout experiment

  Scenario: User sees the control layout in the bridge token selector
    Given the token selector balance layout experiment is in the control variant
    And the user opens the Bridge flow
    When the token selector list is shown
    Then the fiat balance is shown on the top row
    And the token balance is shown on the bottom row with the ticker included

  Scenario: User sees the treatment layout in the bridge token selector
    Given the token selector balance layout experiment is in the treatment variant
    And the user opens the Bridge flow
    When the token selector list is shown
    Then the token balance is shown on the top row without the duplicate ticker
    And the fiat balance is shown on the bottom row

  Scenario: Analytics include the active experiment
    Given the token selector balance layout experiment is active
    When the user opens the Bridge flow
    And submits a bridge quote
    Then the relevant page-view and submit analytics payloads include active_ab_tests for the active experiment
```

## **Screenshots/Recordings**

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

### **Before**

Control variant:
<img width="322" height="683" alt="Screenshot 2026-03-18 at 19 11 16"
src="https://github.com/user-attachments/assets/572bffbe-8ab3-446c-8da8-564cd0ded31d"
/>


### **After**

Treatment variant:
<img width="317" height="690" alt="Screenshot 2026-03-19 at 18 24 35"
src="https://github.com/user-attachments/assets/46b6241d-dd00-4ee1-8de6-7097a7533d14"
/>


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Moderate risk: changes token list balance rendering logic and adds new
`activeAbTests` payloads to bridge submit/page-view analytics paths,
which could affect UI presentation and event schemas if mismatched.
> 
> **Overview**
> Adds a new A/B experiment
(`swapsSWAPS4242AbtestTokenSelectorBalanceLayout`) to vary the Bridge
token selector balance layout: control keeps fiat on the top row with
the token ticker included, while treatment moves token balance to the
top row and optionally removes the ticker.
> 
> Updates bridge analytics plumbing to include a consolidated
`active_ab_tests` array on swap page-view events and to forward
`activeAbTests` through `useSubmitBridgeTx` into
`BridgeStatusController.submitTx`/`submitIntent`, with expanded unit
tests covering both control/treatment behavior and payload propagation.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2508c8c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=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 several E2E page objects (Predict flows and
`TransactionPayConfirmation`) from Detox-only elements/gestures to the
new unified `encapsulated` element model with `UnifiedGestures`, adding
Appium/Playwright locators and framework-conditional actions via
`encapsulatedAction`.
> 
> Extends selectors by introducing
`PredictBalanceSelectorsText.AVAILABLE_BALANCE` (sourced from
`enContent`) and updates Predict market list/details interactions to use
category-label mapping, outcome-button ancestor/XPath matching, and
additional visibility/assertion helpers. Also adds a
`TabBarComponent.tapBrowser()` helper and tightens `tapWallet()`
validation to assert `WalletView.totalBalance` visibility.
> 

## **Changelog**

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

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

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

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Moderate risk because it refactors E2E test page objects and gesture
plumbing to a new cross-framework abstraction; failures will surface as
flaky/broken tests across Detox/Appium rather than runtime app issues.
> 
> **Overview**
> Migrates several E2E page objects (Predict market list/details and
`TransactionPayConfirmation`) from Detox-only elements/gestures to the
unified `encapsulated` element model, adding Appium/Playwright locators
and using `UnifiedGestures` plus `encapsulatedAction` where
Detox-specific delays/scrolling are needed.
> 
> Extends Predict selectors with
`PredictBalanceSelectorsText.AVAILABLE_BALANCE` and updates Predict
interactions to use category label mapping, ancestor/XPath outcome
targeting, and new visibility/assertion helpers (e.g., tab content
checks, volume label).
> 
> Enhances the gesture abstraction by allowing `UnifiedGestureOptions`
to pass Detox-only `scrollToElement` parameters (`direction`,
`scrollAmount`), and adds a `TabBarComponent.tapBrowser()` convenience
method.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cb77792. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#27677)

…ailwind CSS

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

- Component migration (View → Box, StyleSheet → twClassName/tw.style(),
deleted styles.ts)
- Test optimizations (consolidated setup functions, removed duplicates,
cleaner descriptions)
- Added @metamask/design-system-twrnc-preset mock

## **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 ManualBackupStep1 to design system components
and Tailwind CSS

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: ManualBackupStep1 seed phrase reveal screen

  Background:
    Given I have just created a new MetaMask wallet
    And I am on the "Secure your wallet" screen (step 2 of 3)

  Scenario: user sees blurred seed phrase before revealing
    Given the seed phrase is hidden behind a blur overlay

    Then I should see the title "Write down your Secret Recovery Phrase"
    And I should see "Tap to reveal your Secret Recovery Phrase"
    And the "Continue" button should be disabled

  Scenario: user reveals seed phrase by tapping the blur overlay
    Given the seed phrase is hidden behind a blur overlay

    When user taps the blur overlay
    Then the blur overlay should disappear
    And I should see 12 seed phrase words displayed in a 3-column grid
    And the "Continue" button should be enabled

  Scenario: user proceeds to seed phrase confirmation after revealing
    Given user has tapped the blur overlay to reveal the seed phrase

    When user taps the "Continue" button
    Then I should be taken to the seed phrase confirmation screen (step 3 of 3)

  Scenario: user opens the Secret Recovery Phrase definition
    Given the seed phrase reveal screen is visible

    When user taps the "Secret Recovery Phrase" inline link in the description
    Then a modal explaining what a Secret Recovery Phrase is should appear

  Scenario: user skips backup during onboarding with no funds
    Given I am on the seed phrase reveal screen during onboarding
    And my wallet has no funds

    Then I should see a "Remind me later" button

    When user taps "Remind me later"
    Then I should be taken past the backup flow to the onboarding success screen
```

## **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="467" height="983" alt="Screenshot 2026-03-19 at 3 45 57 PM"
src="https://github.com/user-attachments/assets/b21fa35f-c23a-49e8-b5dd-3e230c497957"
/>
<img width="504" height="1021" alt="Screenshot 2026-03-19 at 3 46 03 PM"
src="https://github.com/user-attachments/assets/22b2c428-49d5-4c3b-96ad-5d7fb1c7e0e3"
/>
<img width="463" height="1008" alt="Screenshot 2026-03-19 at 3 46 27 PM"
src="https://github.com/user-attachments/assets/6f74de32-7ade-4099-b98a-3d7107248e71"
/>
<img width="546" height="977" alt="Screenshot 2026-03-19 at 3 46 31 PM"
src="https://github.com/user-attachments/assets/2360922a-6e67-40b2-93cd-7a45a24a4eec"
/>


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Refactors a security-sensitive SRP backup screen’s UI (seed phrase
reveal + password unlock) to new design-system primitives and tailwind
styling, which could introduce subtle layout/accessibility regressions
across platforms/themes. Core navigation and seed phrase handling logic
is largely unchanged but should be spot-checked in-app.
> 
> **Overview**
> Migrates `ManualBackupStep1` from bespoke `StyleSheet`/legacy UI
components to `@metamask/design-system-react-native` primitives (`Box`,
`Button`, `TextField`, `TextButton`, `Icon`) and tailwind-driven styling
via `useTailwind`, and removes the dedicated `styles.ts`.
> 
> Updates the seed-phrase reveal and password-confirmation views to the
new components (including Android-specific bottom spacing and some
accessibility props), and rewrites/streamlines the Jest tests and
snapshots (new tailwind mock, consolidated setup helpers, updated theme
snapshot expectations).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
45d544d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…7671)

## **Description**

When navigating to a market with an existing position, Long/Short
buttons briefly flashed before switching to Modify/Close. Root cause:
`positions` in `usePerpsLivePositions` was derived from `rawPositions`
via a `useEffect`, creating a one-frame lag where `isInitialLoading` was
`false` but `positions` still held stale data. Fix: replace the
state+effect pattern with `useMemo` for synchronous derivation.

## **Changelog**

CHANGELOG entry: Fixed button flash (Long/Short briefly visible) when
opening a market with an existing position

## **Related issues**

Fixes:
[TAT-2236](https://consensyssoftware.atlassian.net/browse/TAT-2236)

## **Manual testing steps**

```gherkin
Feature: Market detail action buttons
  Scenario: Open market with existing position
    Given I have an open BTC position
    When I navigate to the BTC market detail screen
    Then I see Modify/Close buttons immediately
    And I do not see Long/Short buttons flash

  Scenario: Open market without position
    Given I have no position on SOL
    When I navigate to the SOL market detail screen
    Then I see Long/Short buttons immediately
```

## **Screenshots/Recordings**

### **Before**
See automation/27671/before.mp4

### **After**
See automation/27671/after.mp4

## **Pre-merge author checklist**

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

## **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**
> Low risk: small hook refactor to compute `positions` synchronously
plus a targeted regression test; main risk is subtle rendering behavior
changes around initial WebSocket updates.
> 
> **Overview**
> Fixes a one-render mismatch in `usePerpsLivePositions` where
`isInitialLoading` could flip to `false` before derived `positions`
updated, causing a brief Long/Short button flash when opening a market
with an existing position.
> 
> `positions` is now derived from `rawPositions` and `priceData` via
`useMemo` (instead of state set in an effect), and a new regression test
asserts that the first WebSocket update makes `isInitialLoading=false`
and `positions` available in the same render.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
997d441. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#27600)

<!--
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 RevealPrivateCredential component from legacy React Native
styling patterns (View, StyleSheet, component-library Text) to the
MetaMask design system (Box, design system Text with semantic color
tokens, Tailwind twClassName).

What changed:

* Replaced View with Box from @metamask/design-system-react-native.
* Replaced component-library Text, TextVariant, TextColor with design
system equivalents (BodyMD→BodyMd, BodySM→BodySm,
TextColor.Alternative→TextColor.TextAlternative,
TextColor.Default→TextColor.TextDefault, raw
colors.primary.default→TextColor.PrimaryDefault)
* Replaced inline style={styles.wrapper} and style={styles.rowWrapper}
with twClassName props
* Removed @ts-expect-error workaround that was needed for the old View
style array

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

## **Changelog**

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

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

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

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Reveal Secret Recovery Phrase

  Scenario: user views SRP introduction screen
    Given user is logged into the wallet
    When user navigates to Settings > Security & Privacy > Reveal Secret Recovery Phrase
    Then the SRP quiz introduction screen is displayed with correct styling

  Scenario: user completes quiz and enters password
    Given user is on the SRP quiz introduction screen
    When user completes the 2-question security quiz
    Then the password entry screen is displayed with warning banner and correct padding

  Scenario: user reveals SRP after authentication
    Given user is on the password entry screen
    When user enters correct password and taps Confirm
    Then the SRP is displayed in text/QR tabs with "Done" button
    And text colors and spacing match the original design

  Scenario: user copies SRP to clipboard
    Given user has revealed the SRP
    When user taps "Copy to clipboard"
    Then a toast notification confirms the copy action
```

## **Screenshots/Recordings**

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

### **Before**


https://github.com/user-attachments/assets/cc4f5d32-0190-426d-afdc-a753d0e25995


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

### **After**


https://github.com/user-attachments/assets/96de7124-ee4d-4a4e-a994-2c8355027fd5


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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Mostly a UI refactor, but it touches the Secret Recovery Phrase reveal
screen; layout/token changes could inadvertently affect visibility,
spacing, or test coverage for a security-sensitive flow.
> 
> **Overview**
> Migrates `RevealPrivateCredential` from legacy React Native
`View`/component-library `Text` usage to the MetaMask design system,
swapping in `Box` + design-system `Text` and updating typography/color
tokens and wrapper layout to `twClassName`.
> 
> Adds snapshot coverage for three key states (intro, password entry,
and unlocked SRP view) and checks in the new
`RevealPrivateCredential.test.tsx.snap` file to lock down rendering
output.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
79feaf6. Configure
[here](https://cursor.com/dashboard?tab=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**
https://consensyssoftware.atlassian.net/browse/RWDS-1119
Fetch minimum required client versions from backend and prompt user to
update if app version is not met


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

## **Changelog**

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

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

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

CHANGELOG entry: null 

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**
<img width="1179" height="2556" alt="Simulator Screenshot - E2E Test -
2026-03-18 at 19 29 55"
src="https://github.com/user-attachments/assets/8ee44e97-a20c-4298-aa64-6b151eccfb03"
/>

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a backend-driven minimum-version check that can block entry into
the Rewards flow and introduces new API/messenger wiring plus Redux
state to support it. Risk is moderate because it changes navigation
behavior and introduces a new network call/caching path in the Rewards
controller/data service.
> 
> **Overview**
> Adds a **Rewards version guard**: the app fetches minimum client
version requirements from a new public Rewards API endpoint and stores
them in Redux, then uses `selectIsRewardsVersionBlocked` to determine if
the current app version is allowed.
> 
> When blocked, `RewardsNavigator` now short-circuits normal
navigation/rendering and shows a new `RewardsUpdateRequired` screen with
an update CTA (App Store/Play Store) and new MetaMetrics events for
view/click tracking.
> 
> Extends the Rewards engine stack (data service + controller +
messenger + types) with `getClientVersionRequirements`, adds
version-guard reducers/selectors and tests, and refactors candidate
subscription ID reset logic to preserve the new version-guard fields
(plus other non-subscription-scoped state).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3c9d911. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
This PR resolves three tickets in wallet/cash/multichain flows:
- TMCU-565: fixed `Token Details Opened` source for aggregated mUSD row
to `home_section`.
- TMCU-556: added missing `cta_click_target: 'cta_button'` to `mUSD
Conversion CTA Clicked`.
- TMCU-429: standardized multichain address-copy toast text to `Address
copied to clipboard`.

## **Changelog**
CHANGELOG entry: Fixed several wallet regressions affecting mUSD
analytics tracking and address copy messaging consistency

## **Related issues**
Refs: TMCU-565
Refs: TMCU-556
Refs: TMCU-429

## **Manual testing steps**
```gherkin
Feature: Wallet segment regressions

  Scenario: mUSD aggregated row tracks correct source
    Given user is on Wallet Home with Cash section visible
    When user taps the mUSD aggregated row
    Then token details opens
    And source is tracked as home_section

  Scenario: Get mUSD CTA includes click target
    Given user sees Get mUSD CTA in Cash section
    When user taps Get mUSD
    Then MUSD_CONVERSION_CTA_CLICKED is tracked
    And payload includes cta_click_target with value cta_button

  Scenario: Multichain address copy shows consistent toast
    Given user opens Multichain Address List
    When user copies an address
    Then toast shows Address copied to clipboard
```

## **Screenshots/Recordings**
### **Before**
<img width="396" height="839" alt="Screenshot 2026-03-20 at 10 45 36"
src="https://github.com/user-attachments/assets/26c4f30a-6397-414f-8a74-ea98a072c3b8"
/>

### **After**
<img width="390" height="832" alt="Screenshot 2026-03-20 at 10 44 20"
src="https://github.com/user-attachments/assets/b624da9b-a53d-4c5e-9d73-1a7ba66cff75"
/>

## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk: changes are limited to analytics payload/source attribution
and a toast string, with small navigation param updates covered by
tests.
> 
> **Overview**
> Fixes wallet analytics regressions by introducing
`TokenDetailsSource.HomeSection` and using it when navigating to Token
Details from the homepage Cash mUSD aggregated row; Token Details open
tracking now treats this source like token-list entry points for A/B
attribution.
> 
> Adds missing `cta_click_target: 'cta_button'` to the
`MUSD_CONVERSION_CTA_CLICKED` event from the Cash “Get mUSD” CTA (and
updates tests accordingly). Standardizes the multichain address-copy
toast message to `notifications.address_copied_to_clipboard`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
12a5ade. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Patryk Łucka <PatrykLucka@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 the ability to edit gas (network fee) from the cancel/speed up
modal before confirming. Users can tap the network fee row to open the
gas fee modal, adjust gas, then confirm cancel or speed up. A new
context allows the gas modal to operate on the cancel/speed up
transaction instead of only the current approval request.
## Changes
### Cancel/Speed up modal
- **Edit network fee**: When the cancel/speed up modal is open, the
network fee row shows an edit icon and is tappable. Tapping opens the
existing gas fee modal so the user can change gas before confirming.
- **Seeding bump params**: When the modal opens, the transaction is
updated with suggested bump params via `getBumpParamsForCancelSpeedup`
and `updateTransactionGasFees`, so the gas modal shows the recommended
values when the user taps edit.
- **Single source of truth**: `useCancelSpeedupGas` now takes `txId` and
reads the transaction from the store. Gas edits from the gas modal (via
`updateTransactionGasFees`) are reflected in the cancel/speed up modal.
### Gas fee modal transaction context
- **New context**: `GasFeeModalTransactionProvider` and
`useGasFeeModalTransaction` let the gas modal work with a specific
transaction id (e.g. the cancel/speed up tx) instead of only the
approval request.
- **Metadata hook**: `useTransactionMetadataRequest` uses the context;
when `transactionId` is set (e.g. from cancel/speed up), it uses that id
for transaction metadata instead of the approval request id.
### useCancelSpeedupGas refactor
- **API**: Input changed from `{ tx, isCancel }` to `{ txId }`. Hook
reads the transaction with `selectTransactionMetadataById(state, txId)`.
- **Helper**: `getBumpParamsForCancelSpeedup(tx, isCancel,
gasFeeEstimates)` is exported and used to compute bump params when the
cancel/speed up modal opens.
- **Display**: Hook returns params derived from the transaction in the
store so the modal reflects any gas changes made in the gas modal.


### Cancel / Speed Up + Edit Gas — Flow (Happy Path)

**One-line:** Pending Tx → Open modal (seed bump) → [Confirm **or** Edit
gas → Confirm] → Submit.

```mermaid
flowchart TB
  A[Pending Tx] --> B[Open Cancel/Speed Up Modal]
  B --> C[Seed bump params]
  C --> D[Show fee, Edit, Confirm]
  D --> E[Confirm]
  D --> F[Edit]
  F --> G[Gas Modal]
  G --> H[Apply]
  H --> D
  E --> I[Submit speed up/cancel]
```

**Legend**

| Step | What happens |
|------|----------------|
| Seed bump params | `getBumpParamsForCancelSpeedup` +
`updateTransactionGasFees` so modal shows suggested fee |
| Current params | From store via `useCancelSpeedupGas(txId)`; updates
when user edits in gas modal |
| Gas Modal | Wrapped in `GasFeeModalTransactionProvider(transactionId)`
so it edits the cancel/speedup tx |

**Data flow (compact)**

- **Modal → store:** On open, bump params written to tx via
`updateTransactionGasFees`.
- **Gas modal → store:** User changes applied via same util;
`useCancelSpeedupGas` reads tx from store, so fee row updates.
- **Confirm:** `onConfirm(paramsForController)` uses whatever is on the
tx (default bump or user-edited).

<!--
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: Added edit gas from cancel/speed up modal: users can
tap the network fee row to open the gas fee modal and adjust gas before
confirming cancel or speed up

## **Related issues**

Fixes: MetaMask/mobile-planning#2419
https://consensyssoftware.atlassian.net/browse/CONF-1007

## **Manual testing steps**

```gherkin
Scenario: User opens cancel/speed up modal and sees editable network fee
  Given the user has a pending transaction
  And the user opens the cancel or speed up modal for that transaction
  Then the modal shows the current network fee (native and fiat)
  And the network fee row shows an edit icon
  And the network fee row is tappable

Scenario: User edits gas from cancel/speed up modal
  Given the user has a pending transaction
  And the user has opened the cancel or speed up modal
  When the user taps the network fee row (or the edit icon)
  Then the gas fee modal opens
  And the gas fee modal shows the suggested bump values for that transaction
  When the user changes gas (e.g. max fee or priority fee) in the gas modal
  And the user confirms or applies in the gas modal
  Then the gas modal closes
  And the cancel/speed up modal shows the updated network fee
  When the user taps the confirm button in the cancel/speed up modal
  Then the cancel or speed up is submitted with the user-edited gas

```

## **Screenshots/Recordings**

[Screencast from 2026-03-09
11-22-02.webm](https://github.com/user-attachments/assets/5cccd075-509e-4b9d-a7fe-75a295fd661e)


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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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 transaction gas-fee handling by seeding/updating on-chain fee
params and routing the gas modal to a different transaction via new
context; mistakes could lead to incorrect fee values being applied to
cancel/speed-up submissions.
> 
> **Overview**
> Adds an **editable network fee** flow to the cancel/speed up modal:
the network fee row becomes tappable (with an edit icon) and opens the
existing `GasFeeModal` to adjust gas before confirming.
> 
> When the modal opens, it now **seeds bumped gas params** onto the
pending tx via `getBumpParamsForCancelSpeedup` +
`updateTransactionGasFees`, and `useCancelSpeedupGas` is refactored to
take a `txId` and read current gas fields from the store so UI/confirm
uses whatever the gas modal last wrote.
> 
> Introduces
`GasFeeModalTransactionProvider`/`useGasFeeModalTransaction` so gas
modal internals can target a specific transaction (not just the approval
request), and updates `useTransactionMetadataRequest` to honor this
override. Tests are updated/added to cover the new hook API, bump-param
helper, and gas modal dismissal when the parent modal closes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3d80379. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **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?
-->

This PR fixes Bridge source amount editing so keypad insertion and
deletion respect the user-selected cursor position, including when the
displayed amount contains locale formatting such as grouping separators.

The change keeps shared keypad components untouched and instead adds
Bridge-local cursor handling:
- adds `useSourceAmountCursor` to own source cursor state and keypad
edit behavior
- adds raw/formatted cursor mapping utilities so the displayed caret
stays aligned with the raw editable amount
- adds `applyKeyAtCursor` so insert/delete/decimal edits happen
deterministically at the raw cursor position
- updates `TokenInputArea` to support source `selection` and
`onSelectionChange`
- updates gasless quick-pick presets to replace the full amount
explicitly instead of overloading keypad edit semantics

## **Changelog**

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

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

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

CHANGELOG entry: Fixed a bug where Bridge amount edits did not respect
the cursor position when using the keypad.

## **Related issues**

Fixes:
[SWAPS-4131](https://consensyssoftware.atlassian.net/browse/SWAPS-4131)

## **Manual testing steps**

```gherkin
Feature: Bridge source amount cursor-aware editing

  Scenario: insert a digit in the middle of a formatted amount
    Given the user opens Bridge
    And the source token is set
    And the user enters 1234 in the source amount
    And the displayed amount shows as 1,234
    When the user places the cursor between 2 and 3
    And the user taps 9 on the keypad
    Then the source amount updates to 12,934

  Scenario: delete a digit in the middle of a formatted amount
    Given the user opens Bridge
    And the user enters 1234 in the source amount
    And the displayed amount shows as 1,234
    When the user places the cursor after 2
    And the user taps backspace on the keypad
    Then the source amount updates to 134

  Scenario: select a quick-pick preset after editing the amount
    Given the user opens Bridge on a gas-sponsored quote path
    And quick-pick preset buttons are visible
    When the user taps a preset percentage button
    Then the source amount updates to the preset amount

  Scenario: apply Max after moving the cursor
    Given the user opens Bridge
    And the user moves the cursor in the source amount field
    When the user taps Max
    Then the source amount updates to the available max balance
    And subsequent keypad edits continue to work correctly
```

## **Screenshots/Recordings**

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

### **Before**

N/A

### **After**


https://github.com/user-attachments/assets/1c06fb3e-db39-42eb-bec0-ae9d6601db16

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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.

<!-- Generated with the help of the pr-description AI skill -->


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how Bridge source amounts are edited and selected, including
cursor mapping between formatted display values and raw input, which
could impact entered swap amounts. Covered by new unit tests but touches
core user input behavior.
> 
> **Overview**
> Fixes Bridge source-amount editing so keypad insert/delete operations
apply at the user-selected caret position even when the displayed amount
includes locale formatting (grouping/decimal separators).
> 
> Adds `useSourceAmountCursor` plus new cursor-mapping and edit
utilities (`cursorPosition`, `applyKeyAtCursor`) and wires controlled
`selection`/`onSelectionChange` through `TokenInputArea`. Also updates
Max/preset flows to normalize amounts via
`normalizeSourceAmountToMaxLength` and changes gasless quick-pick
presets to explicitly replace the amount (new `onAmountSelect` +
`tokenBalance` prop), with updated/added tests.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
166c4cd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: George Gkasdrogkas <georgegkas@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 is the first step for introducing the new feature called "What's
Happening", it's under a feature flag and still under development.

<img height="800" alt="Simulator Screenshot - iPhone 16e - 2026-03-17 at
15 28 54"
src="https://github.com/user-attachments/assets/a87ec91a-18b0-4aca-b488-56ef389d5743"
/>


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

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Adds a new homepage section that fetches remote AI market overview
data and introduces a new navigation route, which could affect homepage
rendering/refresh behavior and network load when the flag is enabled.
> 
> **Overview**
> Adds a new **feature-flagged** `What's happening` carousel section to
the homepage, including integration into section ordering, section-index
analytics (`HomeSectionNames.WHATS_HAPPENING`), and homepage-wide
refresh.
> 
> Introduces `WhatsHappeningSection` UI with loading skeletons, error
retry state, and card rendering, backed by a new `useWhatsHappening`
hook that calls `AiDigestController.fetchMarketOverview()` and maps
trends into carousel items.
> 
> Adds a `selectWhatsHappeningEnabled` remote flag selector, a new
`Routes.WHATS_HAPPENING_DETAIL` navigation target (placeholder params),
English strings for the section/categories, and bumps
`@metamask/ai-controllers` to `^0.4.0`, with unit tests covering the new
hook/components.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ddcbe6a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ns [RWDS-1077] (#27676)

## Summary

- **Shared geo-restriction list**: Extracts `ONDO_RESTRICTED_COUNTRIES`
from `useRwaTokens` into `app/util/ondoGeoRestrictions.ts` — a single
source of truth consumed by both the RWA token hook and the campaign
opt-in sheet.
- **Geolocation status selector**: Adds `selectGeolocationStatus` to the
`geolocationController` selector module so components can observe the
`idle / loading / complete` lifecycle.
- **Campaign opt-in guard**: `CampaignOptInSheet` now reads geolocation
and blocks the opt-in CTA while geo is still resolving (shows _"Checking
region…"_), then shows an info banner and disables the CTA once the user
is confirmed to be in a restricted country.
- **Tests**: Unit tests for `ONDO_RESTRICTED_COUNTRIES`, the new
selector, and all geo-loading / geo-restriction paths in
`CampaignOptInSheet`.

## Changelog

CHANGELOG entry: Added geo-restriction guard to campaign opt-in sheet,
disabling opt-in for users in restricted regions

## Related issues

Fixes:

## UI — geo-restriction banner

<img width="1036" height="965" alt="Screenshot from 2026-03-19 11-07-20"
src="https://github.com/user-attachments/assets/7e1fd4c7-bfc0-42d5-80b0-f169d456101c"
/>

> The campaign opt-in sheet when the user's detected country is in the
restricted list. The info banner reads _"Not available in your region —
This campaign is not available in your region due to local
regulations."_ and the **Opt in** button is disabled.

## Manual testing steps

- [ ] Device in a restricted country (e.g. set geolocation to `US`) →
banner shown, CTA disabled
- [ ] Device in an allowed country (e.g. `AU`) → no banner, CTA enabled
- [ ] Geolocation still loading → CTA shows _"Checking region…"_ and is
disabled; no banner
- [ ] DEV mode → restriction bypassed (banner never shown)
- [ ] Unit tests pass: `yarn jest ondoGeoRestrictions
geolocationController/index CampaignOptInSheet`

## Screenshots/Recordings

### Before

### After

See UI — geo-restriction banner section above.

## Pre-merge author checklist

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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.

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds gating logic that can disable campaign opt-in based on
geolocation state/country, which directly affects user eligibility and
conversion. Risk is mitigated by DEV bypass and expanded unit test
coverage for loading/restricted/allowed scenarios.
> 
> **Overview**
> Adds a geo-restriction guard to `CampaignOptInSheet`: the CTA now
shows *Checking region…* and is disabled while geolocation is
`idle/loading`, and shows a `RewardsInfoBanner` + disables opt-in when
the user is in a restricted region (including `ONDO_HOLDING` using the
shared Ondo restricted list, with `__DEV__` bypass).
> 
> Extracts `ONDO_RESTRICTED_COUNTRIES` into
`app/util/ondoGeoRestrictions.ts` (consumed by both `useRwaTokens` and
campaign opt-in), adds `selectGeolocationStatus`, and updates
translations/tests to cover the new selector, shared list, and
geo-loading/restriction behaviors.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3c269bd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: sophieqgu <sophieqgu@gmail.com>
)

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

**Problem**

When the user navigates to a token's details page and then opens Swap
from there, the navigation stack looks like:

```
[Home → Asset(TokenA) → Bridge]
```

Tapping a trending token inside the Swap/Bridge screen calls
`navigation.navigate('Asset', tokenBParams)`. React Navigation's
`navigate` traverses the stack looking for an existing matching route —
it finds the already-present `Asset(TokenA)` screen and navigates
**back** to it, dismissing the Bridge screen. The user ends up on the
original token's details page instead of the tapped trending token's
details page.

**Fix**

Replace `navigation.navigate('Asset', ...)` with
`navigation.dispatch(StackActions.push('Asset', ...))` in
`TrendingTokenRowItem`. `StackActions.push` always creates a **new**
screen instance regardless of what is already on the stack, so the
navigation flows correctly forward to the tapped token. When `push` is
dispatched from a nested navigator that does not register `'Asset'`
(e.g. `BridgeScreenStack` or `ExploreHome`), React Navigation bubbles
the action up to the root modal stack which does register it — matching
the behaviour already used in `BridgeTokenSelector` for the same reason.

Tests are updated to assert that `navigation.dispatch` is called with
the correct `StackActions.push` action and that `navigation.navigate` is
no longer called.

## **Changelog**

CHANGELOG entry: Fixed tapping a trending token from the Swap screen
dismissing Swap instead of opening the token details page

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Trending token navigation from Swap screen

  Background:
    Given I am logged into MetaMask Mobile
    And I have at least one token in my wallet

  Scenario: user navigates to a trending token from Swap opened via token details page
    Given I am on a token details page (e.g. mUSD)
    And I can see the "Swap" action button

    When user taps "Swap"
    Then the Swap screen opens
    And I can see a list of trending tokens

    When user taps on a trending token (e.g. Sigma)
    Then the Sigma token details page opens
    And the Swap screen is no longer visible

  Scenario: user navigates to a trending token from Swap opened directly (not from token details)
    Given I am on the Wallet home screen

    When user opens the Swap screen directly
    And user taps on a trending token (e.g. Everlyn Token)
    Then the Everlyn Token details page opens

  Scenario: user navigates to a trending token from the Explore tab
    Given I am on the Explore tab

    When user taps on a trending token
    Then the token details page for that token opens
```

## **Screenshots/Recordings**

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

### **Before**

Tapping a trending token from Swap (opened via token details page)
dismisses Swap and returns to the original token details page.

### **After**

Tapping a trending token from Swap navigates forward to the tapped
token's details page.

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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.

<!-- Generated with the help of the pr-description AI skill -->


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes navigation behavior for trending token taps by forcing a new
`Asset` screen onto the stack; risk is mainly UX/regression if the
action doesn’t bubble correctly in some navigators.
> 
> **Overview**
> Fixes trending-token taps to **always open a new token details
screen** by replacing `navigation.navigate('Asset', ...)` with
`navigation.dispatch(StackActions.push('Asset', ...))` in
`TrendingTokenRowItem`.
> 
> Updates `TrendingTokenRowItem` tests to mock `dispatch` and assert
`StackActions.push` is used (and `navigate` is not), including cases
where a popular network must be added first or navigation is suppressed
on failure.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e2f50ab. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…27581)

## **Description**

Migrates `AccountBackupStep1B` from legacy React Native primitives to
the MetaMask design system. Replaces raw `View` components with `Box`,
removes `StyleSheet.create()` in favor of `twClassName` Tailwind
utilities, and adds the `useTailwind` hook. Also fixes a precommit
violation by replacing the static `Dimensions.get('window')`
module-level call with the `useWindowDimensions()` hook (so layout
responds to rotation/resize), removes redundant
`BoxFlexDirection.Column` props, and replaces all arbitrary Tailwind
bracket values (e.g. `text-[32px]`, `leading-[17px]`) with design system
scale steps.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Account Backup Step 1B screen

  Scenario: Visual parity after design system migration
    Given the user has created a new wallet and reached the backup prompt
    When the AccountBackupStep1B screen is displayed
    Then the layout, typography, and spacing appear identical to the pre-migration design
    And the "Why is this important?" info button is tappable and opens the modal
    And the modal close button dismisses the modal
    And the "Start" CTA navigates to ManualBackupStep1
    And the SRP explanation link opens the SRP info sheet

  Scenario: Responsive layout on device rotation
    Given the user is on the AccountBackupStep1B screen
    When the device is rotated to landscape
    Then the seed phrase illustration resizes correctly without layout overflow
```

## **Screenshots/Recordings**

|             | Before | After |
|-------------|--------|-------|
| **Light** | <img
src="https://github.com/user-attachments/assets/d9dd926a-5e4f-4673-b975-ff658ee771d4"
width="300"/> | <img
src="https://github.com/user-attachments/assets/742d3e28-5fd8-4a68-b51f-09e7f22699d0"
width="300"/> |
| **Dark** | <img
src="https://github.com/user-attachments/assets/2fcd0028-6a67-413c-8cd1-75bab51afac4"
width="300"/> | <img
src="https://github.com/user-attachments/assets/123fa568-aa2f-434c-908d-92ed6b39464a"
width="300"/> |

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk refactor limited to a single onboarding screen, primarily
swapping UI primitives/styling; main risk is visual or interaction
regressions (modal/CTA/link) across device sizes/rotation.
> 
> **Overview**
> Migrates `AccountBackupStep1B` to the MetaMask React Native design
system by replacing legacy `View`/`StyleSheet`/custom button usage with
`Box`, design-system `Text`, `Button`, `Icon`, and Tailwind
(`useTailwind`) styling.
> 
> Updates the seed phrase illustration sizing to use
`useWindowDimensions()` (instead of a module-level `Dimensions`
constant) so layout recalculates on resize/rotation, and refactors
repeated body/bullet copy into small helper components (`BodyXsText`,
`BulletText`) while keeping navigation and metrics wiring intact.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5324d96. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**
Avoids full token catalog for non-withdraw flows.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1067

## **Manual testing steps**
1. Predict and Perps deposit pages load faster now

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: this only changes when `useSendTokens` loads the full token
catalog, and is gated to withdraw flows with an allowlist; main risk is
missing allowlisted tokens if the gating condition is wrong.
> 
> **Overview**
> **Improves token list performance for non-withdraw flows** by avoiding
loading the full token catalog via `useSendTokens` unless the current
transaction is a pay-withdraw *and* a withdraw allowlist
(`config.tokens`) is configured.
> 
> Updates `useWithdrawTokenFilter` to conditionally enable
`includeNoBalance`/`includeAllTokens`, and extends tests to assert the
new `useSendTokens` call behavior (including clearing mocks between
cases).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
82a19fc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
…#27584)

## **Description**

Replaces legacy styling and icon primitives in `ProtectYourWalletModal`
with design system components, keeping the existing `PureComponent` +
`connect()` + `withAnalyticsAwareness` architecture unchanged.

**What changed (visual layer only):**
- `View` → `Box` from `@metamask/design-system-react-native`
- `StyleSheet.create()` + theme-based color styles → `twClassName`
Tailwind utilities (DS handles theming internally)
- Raw `Text` with `fontStyles` → DS `Text` with `TextVariant`,
`TextColor`, `FontWeight`
- `FontAwesome` icon + `TouchableOpacity` (close button) → DS
`ButtonIcon`
- `TouchableOpacity` + `Text` (Learn more) → DS `Button`
(`ButtonVariant.Tertiary`)
- Remove `ThemeContext` / `mockTheme` imports (no longer needed)
- Remove `fontStyles` import (no longer needed)
- PNG `require()` + `eslint-disable` comment → clean ES module import



## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: <!-- no linked issue -->

## **Manual testing steps**

```gherkin
Feature: Protect Your Wallet Modal — visual regression check

  Scenario: Modal renders correctly (light + dark mode)
    Given the user has a wallet without a backed-up seed phrase
    When the wallet home screen loads
    Then the Protect Your Wallet modal appears
    And the title, lock image, body text, close button, and Learn more button are visible
    And the layout matches the before/after screenshots in both light and dark mode

  Scenario: All interactions still work
    Given the modal is visible
    When user taps the X close button — modal dismisses
    When user taps "Protect wallet" — navigates to backup SRP flow
    When user taps "Learn more" — opens MetaMask safety tips webview
    When user taps "Remind me later" — modal dismisses
```


|             | Before | After |
|-------------|--------|-------|
| **Light** | <img
src="https://github.com/user-attachments/assets/58fded8f-b1e3-46c7-a2ab-a7d873268c23"
width="300"/> | <img
src="https://github.com/user-attachments/assets/d3c4ee2f-8806-4803-ab6d-2b71bdc87b3c"
width="300"/> |
| **Dark** | <img
src="https://github.com/user-attachments/assets/ff9d2404-6b35-487a-85c6-81eeafe6af9f"
width="300"/> | <img
src="https://github.com/user-attachments/assets/342f01a9-a860-49f9-ae8f-5404946dff3a"
width="300"/> |


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk UI refactor that replaces legacy styling/components with
design-system `Box`/`Text`/`Button` variants while keeping the same
navigation and analytics flows.
> 
> **Overview**
> Refactors `ProtectYourWalletModal` to use MetaMask design-system
components and Tailwind-style `twClassName` layout instead of legacy
`StyleSheet` + `View`/`TouchableOpacity` + FontAwesome close icon.
> 
> The modal’s structure is preserved (same cancel/confirm handlers,
navigation targets, analytics events, and seedless-onboarding
suppression), but the header close control is now a DS `ButtonIcon`, the
“Learn more” action is a DS `Button` (`Tertiary`), and the image is
imported via ES module rather than `require()`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8571195. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

This PR enables redesigned HW wallet flows for Ledger.

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

```gherkin
Feature: Verify new Ledger flows

  Scenario: user wants to interact with MetaMask and Dapps
    Given user has a Ledger device at hand

    When user wants to either create an account, send, swap or bridge, or even interact with dapps using their Ledger account
    Then user sees the new Ledger flow, and is guided through all steps to get their device ready and confirm the interaction
```

## **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/eb5d2d66-8740-44fb-88b0-4e27877e41b6



https://github.com/user-attachments/assets/d2b63ae5-fe3a-4f81-8349-9a5eae888361



https://github.com/user-attachments/assets/162843b0-70d9-4828-b0d4-2c206aea7c95



https://github.com/user-attachments/assets/cf2df194-fbd2-4893-a42a-42b35b2c4bf4




## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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





<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Moderate risk because it rewires Ledger signing/connection flows and
modal dismissal logic to the new hardware-wallet provider, which can
affect transaction/message approval UX and error handling across
navigation paths.
> 
> **Overview**
> Migrates Ledger confirmation and signing modals to the unified
`useHardwareWallet` flow: `LedgerConfirmationModal` now runs a one-shot
`ensureDeviceReady` + `showAwaitingConfirmation` signing sequence (with
optional `operationType`) and renders no local step UI.
> 
> Updates `LedgerMessageSignModal` and `LedgerTransactionModal` to
remove `ReusableModal` usage, close via navigation `goBack` with
double-navigation guards, and (for transactions) explicitly reject
pending approvals on user rejection.
> 
> Refactors `LedgerSelectAccount` to initialize via `useHardwareWallet`
(`setTargetWalletType`, `ensureDeviceReady`), replace the old
`LedgerConnect`/Bluetooth hook path with a loading state + direct
account fetch/pagination/unlock calls, and adjusts analytics/model-name
derivation accordingly; adds/updates unit tests and removes obsolete
step components, styles, and snapshots.
> 
> Wraps the app `Root` in `HardwareWalletProvider` so the bottom-sheet
driven hardware wallet UX is available globally.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d126e19. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
…ion perspective (#27536)

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

When bridging from a non-EVM chain to an EVM chain (e.g. Solana →
Optimism), the completed bridge transaction was only visible on the
source chain's activity list. Switching to the destination chain's
activity view showed an empty list, even though funds were correctly
received. The transaction would only appear after performing another
unrelated transaction on the destination chain.

**Root cause:** `UnifiedTransactionsView` filtered non-EVM transactions
exclusively by source chain. When viewing Optimism-only activity, the
Solana source transaction was filtered out, and no EVM-side entry
existed for the bridge destination.

**Fix (3 parts):**

1. **Activity list filtering** (`UnifiedTransactionsView.tsx`): Non-EVM
bridge source transactions are now also included when their destination
EVM chain is enabled, by cross-referencing bridge history items.

2. **Destination perspective rendering**
(`MultichainBridgeTransactionListItem.tsx`): When a bridge transaction
is shown from the destination chain's perspective, the component now
displays the destination chain icon, received token amount (e.g. `+0.1
ETH`), and destination token symbol instead of the source chain details.

3. **Balance/data refresh on bridge completion** (`Engine.ts`): When
`BridgeStatusController:destinationTransactionCompleted` fires for an
EVM destination, the handler now also triggers
`AccountTrackerController.refresh` and
`TransactionController.updateIncomingTransactions` to update native
balances and fetch incoming transactions.

## **Changelog**

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

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

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

CHANGELOG entry: Fixed cross-chain bridge transactions not appearing on
the destination chain's activity list

## **Related issues**

Fixes: #23424

## **Manual testing steps**

```gherkin
Feature: Cross-chain bridge activity visibility

  Scenario: Bridge from Solana to Optimism shows on Optimism activity
    Given user has Solana and Optimism networks enabled
    And user has SOL balance on Solana

    When user initiates a bridge from SOL on Solana to ETH on Optimism
    And the bridge completes successfully
    And user switches network filter to show only Optimism activity
    Then the bridge transaction is visible in the Optimism activity list
    And the transaction shows the Optimism network icon
    And the transaction displays the received ETH amount with a "+" prefix

  Scenario: Bridge from Solana to Optimism shows on Solana activity
    Given user has Solana and Optimism networks enabled
    And user has completed a bridge from SOL to ETH

    When user switches network filter to show only Solana activity
    Then the bridge transaction is visible in the Solana activity list
    And the transaction shows the Solana network icon
    And the transaction displays the sent SOL amount

  Scenario: Bridge visible on All Networks view
    Given user has Solana and Optimism networks enabled
    And user has completed a bridge from SOL to ETH

    When user views All Networks activity
    Then the bridge transaction is visible
    And the transaction shows from the source (Solana) perspective

  Scenario: Non-bridge Solana transactions do not leak into EVM-only activity
    Given user has only Optimism network enabled (Solana disabled)
    And user has regular (non-bridge) Solana transactions

    When user views the activity list
    Then the regular Solana transactions are not shown
```

## **Screenshots/Recordings**

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

### **Before**

https://github.com/user-attachments/assets/857223df-1b0a-436a-a3a9-b9a3979fff0d
<!-- [screenshots/recordings] -->

### **After**

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


https://github.com/user-attachments/assets/16e441c8-6437-44b4-aa4b-d7b0393c0a0e


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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 transaction list filtering/rendering and engine-side refresh
triggers on bridge completion, which can affect activity visibility and
balance refresh behavior across networks. Changes are scoped and covered
by new unit tests but still impact core wallet UX.
> 
> **Overview**
> Fixes cross-chain bridge activity visibility by **including non-EVM
bridge source transactions when their *destination EVM chain* is the
currently enabled filter** in `UnifiedTransactionsView`.
> 
> Adds a destination-view rendering mode to
`MultichainBridgeTransactionListItem` (destination network badge +
`+`-prefixed received amount/symbol using destination decimals), and
wires it up based on whether the non-EVM source chain is enabled.
> 
> On `BridgeStatusController:destinationTransactionCompleted` for EVM
assets, `Engine` now also refreshes the account tracker (best-effort)
and triggers `TransactionController.updateIncomingTransactions`; unit
tests were added/updated to cover these behaviors.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e18b71a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#27732)

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

Fixes two recurring CI failures affecting Android and iOS E2E smoke
suites.
### Fix 1: Mockttp `Aborted` errors failing tests after filter removal
                                                            
**Root cause:** CI logs showed abort events firing ~180ms *after*
`removeAbortFilter()` was called and Jest's original handlers
  were restored:                                            
17:47:54.898 Abort filter removed — suppressed 0 mockttp "Aborted"
error(s)
17:47:55.082 Aborted at IncomingMessage.failWithAbortError ← 184ms later
The existing 500ms drain inside `stop()` did not help because it runs
*before* `removeAbortFilter()`. On loaded CI runners (4 CPUs
at 100%), the Node.js event loop delays abort events from destroyed
sockets beyond the drain window. By the time
`removeAbortFilter()` restored Jest's handlers, the queued events fired
straight into Jest as test failures.
**Fix:** `removeAbortFilter()` is now `async` and waits 500ms before
restoring Jest's handlers (`MockServerE2E.ts`).
`withFixtures` now `await`s the call (`FixtureHelper.ts`). Abort events
observed at up to ~200ms fire during the 500ms hold window
and are suppressed by the still-active filter.
                                                            
### Fix 2: Android `adb: device 'emulator-XXXX' not found` crashing
tests
  
**Root cause:** On CI, emulators occasionally go offline between tests
(still booting, brief crash-recovery).
`setupAndroidPortForwarding` had no retry logic — any `adb reverse`
failure was immediately rethrown, propagating through
`startResourceWithRetry` → `handleLocalNodes` → `withFixtures` and
killing the test before it started.
                                                            
Failed to set up Android port forwarding for anvil:
    adb: device 'emulator-10342' not found
Failed to start anvil after 0 attempts
Error in withFixtures: Command failed: adb -s emulator-10342 reverse
tcp:8545 tcp:55324
**Fix:** `setupAndroidPortForwarding` now retries up to 3 times (2s
apart) when the error indicates the device is transiently
unreachable (`not found`, `device offline`, `unauthorized`). Persistent
failures still fail the test after exhausting retries.
                                                            
## Files changed
                                                            
| File | Change |
  |------|--------|                                         
| `tests/api-mocking/MockServerE2E.ts` | `removeAbortFilter()` made
async with 500ms pre-restore delay |
| `tests/framework/fixtures/FixtureHelper.ts` | `await
mockServerInstance.removeAbortFilter()` |
| `tests/framework/fixtures/FixtureUtils.ts` | Retry loop (3×, 2s) in
`setupAndroidPortForwarding` for device-offline errors |

## **Changelog**

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

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

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

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Medium risk because it changes E2E fixture cleanup timing (adds an
awaited 500ms delay) and introduces retry behavior around `adb reverse`,
which can mask persistent device issues if misclassified but is limited
to specific error strings and test infrastructure.
> 
> **Overview**
> Reduces flaky E2E CI failures by making
`MockServerE2E.removeAbortFilter()` async and delaying restoration of
Jest handlers by 500ms, then updating `withFixtures` cleanup to `await`
it so late `mockttp` "Aborted" events don’t fail tests.
> 
> Improves Android stability by adding a 3-attempt (2s backoff) retry
loop around `adb reverse` in `setupAndroidPortForwarding` when devices
are transiently unreachable (e.g., "not found"/"device
offline"/"unauthorized"), while keeping non-transient failures fatal.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e136696. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…7684)

## Description

Adds the **Earn Rewards** section to the Rewards Dashboard and
introduces a dedicated mUSD Calculator screen.

### Changes

- **`EarnRewardsPreview`** — new dashboard section with two geo-gated
earn cards:
- **mUSD card**: navigates to `MusdCalculatorView`; hidden when geo
disallows opt-in (e.g. UK)
- **MetaMask Card card**: opens card onboarding deeplink; shown only
when user's country is supported
- Shows loading skeletons while geo data resolves; hides entirely when
neither card is eligible
- **`MusdCalculatorView`** — new screen wrapping `MusdCalculatorTab`
with standard header; registered as `MUSD_CALCULATOR_VIEW` route
- **`CampaignsPreview`** — simplified to a single "featured" campaign
(priority: active → upcoming → previous) instead of separate active tile
+ upcoming banner; active campaigns now also sorted by soonest start
date
- **`selectCardIsLoaded`** — new selector exported from card redux slice
- Locale strings and test selectors added for the earn rewards section

## Screenshots


- Showing both earn cards (with alt description for card holder):

<img width="958" height="1120" alt="image"
src="https://github.com/user-attachments/assets/d1678cbe-bc32-4184-95b7-bc97baf510e8"
/>

- Showing card if not holder:

<img width="968" height="403" alt="Screenshot from 2026-03-19 15-33-54"
src="https://github.com/user-attachments/assets/694590d0-a21a-49c0-8aec-9188353e52f2"
/>

- Musd calculator page:

<img width="1013" height="2023" alt="image"
src="https://github.com/user-attachments/assets/a5a58960-5171-44bf-9a81-2fcd1297dab3"
/>

## Checklist

- [x] Tests added/updated
- [x] Zero TypeScript errors
- [x] Lint passes
- [x] Locale strings added

## **Changelog**

CHANGELOG entry: Added Earn Rewards preview section to the Rewards
Dashboard with geo-gated mUSD calculator and MetaMask Card earn cards

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



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk due to new navigation route and a dashboard section that
gates UI/behavior on geo/card state and triggers a deeplink, which could
affect user flows if selectors or loading states are incorrect.
> 
> **Overview**
> Adds an **Earn rewards** preview section to the Rewards dashboard,
showing geo-gated cards for the mUSD calculator and MetaMask Card (with
skeleton loading, cardholder-aware copy, and deeplink navigation).
> 
> Introduces a new `MusdCalculatorView` screen and registers it under
the new `Routes.MUSD_CALCULATOR_VIEW` in both `Routes` and
`RewardsNavigator`.
> 
> Simplifies `CampaignsPreview` to render a single *featured* campaign
(active → upcoming → previous) and updates `useRewardCampaigns` to sort
active campaigns by soonest `startDate`, with corresponding test updates
and new i18n/test selectors.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
11004f7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## **Description**

The MetaMask mobile in-app browser currently defaults to Google search,
which shows ads that can lead users to fake/scam websites. This PR
switches the default search engine to Brave Search, providing a
privacy-focused, ad-free search experience that better protects users.

**Changes:**
- Updated `DEFAULT_SEARCH_ENGINE` constant from `'Google'` to `'Brave'`
- Added Brave Search URL (`https://search.brave.com/search?q=`) and
refactored the if/else chain to a module-scoped lookup map
(`SEARCH_ENGINE_URLS`) for cleaner extensibility and to avoid
re-allocation on every call
- Added `'Brave Search'` option to the search engine picker in General
Settings
- Added Redux persist migration (121) to automatically switch all
existing users to Brave, following the same pattern as migration 58
- Updated `processUrlForBrowser` default parameter to use
`AppConstants.DEFAULT_SEARCH_ENGINE` instead of hardcoded `'Google'`
- Updated `SitesSearchFooter` to use the shared `SEARCH_ENGINE_URLS` map
so the "Search for ... on {engine}" label and URL correctly reflect the
selected engine (previously hardcoded to Google/DuckDuckGo only)
- Fallback for unknown search engine values now uses
`AppConstants.DEFAULT_SEARCH_ENGINE` (Brave) instead of hardcoded Google

## **Changelog**

CHANGELOG entry: Changed the default search engine to Brave Search for a
privacy-focused, ad-free browsing experience

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Brave Search as default search engine

  Scenario: New user searches in the in-app browser
    Given the user has freshly installed the app

    When user types a search keyword in the browser URL bar
    Then the search results are served by Brave Search (search.brave.com)

  Scenario: Existing user is migrated to Brave Search
    Given the user previously had Google or DuckDuckGo as their search engine

    When the user updates the app and launches it
    Then the search engine setting is automatically changed to Brave
    And searches in the in-app browser use Brave Search

  Scenario: Search footer label reflects selected engine
    Given the user is typing a keyword in the browser URL bar

    When the autocomplete dropdown appears
    Then the footer shows "Search for {keyword} on Brave" (not Google)
    And tapping it navigates to search.brave.com

  Scenario: User can still select other search engines
    Given the user is on Settings > General

    When user opens the Search Engine picker
    Then Google, DuckDuckGo, and Brave Search are all available options
    And the user can switch between them
    And the search footer label updates to match the selected engine
```

## **Screenshots/Recordings**

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes core browser URL processing and the default search engine,
plus a state migration that mutates persisted user settings; mistakes
could send users to wrong destinations or override preferences
unexpectedly.
> 
> **Overview**
> **Switches the in-app browser default search engine from Google to
Brave.** This updates `AppConstants.DEFAULT_SEARCH_ENGINE`, adds Brave
to the General Settings search engine picker, and centralizes engine
base URLs in `SEARCH_ENGINE_URLS`.
> 
> Search URL generation is refactored to use the shared lookup (with
fallback to the default) in both `processUrlForBrowser` and
`SitesSearchFooter`, and the footer testID/text are renamed from
Google-specific to a generic `trending-search-footer-search-link`.
> 
> Adds migration `126` to move persisted `settings.searchEngine` from
`Google` to `Brave` for existing users, and updates unit/integration/e2e
tests and selectors to match the new engine + testIDs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
471a8dd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

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

After the quote card was updated to match Figma (#21656), horizontal
padding was removed from `quoteContainer`, so `QuoteDetailsCard` content
uses a **16px** gutter from `QuoteDetailsCard.styles.ts`. The swap token
row wrapper (`inputsContainer` in `BridgeView.styles.ts`) still used
**24px**, so the large amount text and fiat values sat **8px further
in** than the Rate / Network fee / Slippage rows.

This change sets `inputsContainer.paddingHorizontal` to **16** so the
swap inputs share the same left edge as the quote details (and match the
trending section’s `px-4` gutter). Addresses
[SWAPS-4268](https://consensyssoftware.atlassian.net/browse/SWAPS-4268).

## **Changelog**

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

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

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

CHANGELOG entry: Fixed swap amount fields not lining up horizontally
with swap quote details on mobile

## **Related issues**

Fixes:

Refs:
[SWAPS-4268](https://consensyssoftware.atlassian.net/browse/SWAPS-4268)

## **Manual testing steps**

```gherkin
Feature: Swap / Bridge quote layout

  Scenario: swap amounts align with quote details
    Given I am on the Swap screen with tokens selected and a non-zero source amount
    And an active quote is shown with the quote details card visible

    When I view the source and destination amount rows and the rows below (Rate, Network fee, Slippage, etc.)

    Then the left edge of the amount and fiat text should align with the left edge of the detail labels
```

## **Screenshots/Recordings**

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

### **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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.

<!-- Generated with the help of the pr-description AI skill -->
<!--
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**
Remove token list item V2 FF and token list item V1

<!--
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: remove token list item V2 FF and token list item V1

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-2951

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Medium risk because it removes an A/B-gated codepath and replaces the
token row implementation and related analytics attribution, which can
affect wallet token list rendering, navigation, and metrics across
networks (EVM + non-EVM).
> 
> **Overview**
> Removes the `tokenListItemV2AbtestVersioned` feature flag and all V2
token list item codepaths, making token lists (`TokenList`,
`TokensSection`) always render `TokenListItem`.
> 
> `TokenListItem` is updated to absorb the former V2 layout: it replaces
`AssetElement` with a custom row UI, adds fiat token-price display (EVM
via market data + FX rates; non‑EVM via multichain rates), handles
testnet/"loading" fiat states, and refines secondary-balance behavior
for mUSD conversion and Merkl claim bonus (including claim/CTA
tracking).
> 
> Token details analytics attribution is simplified by dropping
token-list-layout A/B test properties from `TOKEN_DETAILS_OPENED`, and
unit tests/feature-flag registry entries are updated accordingly.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0b37022. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=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**

1. **Typography:** The homepage Cash section mUSD row showed the
aggregated fiat value in bold (`BodyMDBold`), while token list rows
(`TokenListItemV2`) use medium weight for primary fiat (`BodyMDMedium`),
so Cash looked heavier than the rest of the wallet.
2. **Layout:** Spacing and structure (`gap-5` vs avatar, `gap-0.5`
between lines) did not match the two-line token row pattern (`flex-1
ml-5`, `gap-2.5` on each row).
3. **Fiat strings:** `useMusdBalance` formatted currency with
`I18n.locale` (e.g. `en-US`). Token `balanceFiat` uses
`formatWithThreshold` with `getLocaleLanguageCode()` elsewhere in the
assets flow. That mismatch could produce inconsistent currency symbols
(e.g. `US$` vs `$`) between Cash and token rows.

**Solution**

- **`MusdAggregatedRow`:** Match `TokenListItemV2`’s text column:
`flex-1 ml-5`, two rows with `justifyContent={Between}` and
`twClassName="gap-2.5"`; primary fiat uses `BodyMDMedium`; the second
row wraps claim / spinner / APY in an inner `Box` so the token amount
stays right-aligned like the list.
- **`useMusdBalance`:** Replace `I18n.locale` with
`getLocaleLanguageCode()` for all `formatWithThreshold` fiat formatting
(per-chain and aggregated) so Cash uses the same locale strategy as
token fiat balances.

## **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: Updated Cash (mUSD) row typography and layout to match
the token list and aligned aggregated mUSD fiat formatting with token
balance locale handling.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-579

## **Manual testing steps**

```gherkin
Feature: Cash mUSD row matches token list

  Scenario: Primary fiat weight matches token rows
    Given the user sees the Cash section with mUSD balance (Homepage sections enabled, eligible for Cash)
    When they view the mUSD row
    Then the aggregated fiat amount uses medium weight (same as token list fiat), not bold

  Scenario: Layout and spacing align with token list rows
    Given the same Cash mUSD row is visible
    When comparing to any token row in the token list
    Then the two-line layout (title + fiat on first row; secondary info + token amount on second) and spacing feel consistent

  Scenario: Fiat symbol matches token list formatting
    Given primary currency is USD and device locale is set (e.g. en-US)
    When viewing Cash aggregated fiat and a token’s fiat balance on the same screen
    Then both use the same currency symbol style (e.g. $ consistently, not US$ on Cash only)
```

## **Screenshots/Recordings**

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

### **Before**
<img width="393" height="837" alt="Screenshot 2026-03-20 at 13 14 17"
src="https://github.com/user-attachments/assets/9fe95f9c-1eb0-4066-a011-85c897a27f63"
/>

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

### **After**
<img width="395" height="835" alt="Screenshot 2026-03-20 at 13 10 50"
src="https://github.com/user-attachments/assets/e402979c-87f8-4e82-aeaa-4f60c0e014ff"
/>

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

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk UI/formatting changes: adjusts Cash mUSD row
layout/typography and switches fiat formatting locale helper; no
business logic or data flows are changed beyond display formatting.
> 
> **Overview**
> Updates the Homepage Cash section’s mUSD aggregated row to visually
match `TokenListItemV2`, including spacing, two-line structure, and
changing the primary fiat amount from bold to medium weight while
keeping the right-side token amount aligned.
> 
> Standardizes mUSD fiat formatting in `useMusdBalance` by replacing
`I18n.locale` with `getLocaleLanguageCode()` for per-chain and
aggregated `formatWithThreshold` calls, aligning currency symbol/locale
behavior with token list fiat displays.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8305a44. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Bumps `@metamask/keyring-api` from `^21.5.0` to `^21.6.0`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

N/A

## **Screenshots/Recordings**

Not applicable

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk dependency bump with no application code changes; any risk is
limited to behavior changes inside `@metamask/keyring-api` impacting
keyring interactions at runtime.
> 
> **Overview**
> Updates the `@metamask/keyring-api` dependency from `^21.5.0` to
`^21.6.0`.
> 
> Refreshes `yarn.lock` to resolve `@metamask/keyring-api@21.6.0`
(including updated lockfile resolution/checksum entries).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
06c3afa. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=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**

Adds a skeleton loader during quote fetching in the swaps experience.

<!--
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: improves quote loading UI

## **Related issues**

Fixes: null

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk UI/test-only change that adds deterministic `testID`s and a
small unit test for the quote details loading skeleton; no business
logic or data flow changes.
> 
> **Overview**
> Adds a dedicated `QuoteDetailsCardSkeleton` loading state with per-row
`testID`s and re-exports it from `QuoteDetailsCard/index.ts`.
> 
> Updates `QuoteDetailsCard.test.tsx` to cover the skeleton, asserting
the `QUOTE_DETAILS_SKELETON` container renders and contains **four**
loading rows.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4309445. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…27738)

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

Part of the analytics cleanup workstream
([#26686](#26686)).
Delivers [PR
C1](#26812): migrate
`useMetrics` to `useAnalytics` in hardware wallet, wallet recovery,
onboarding-adjacent, and related UI (~27 files).

- **Ledger / Bluetooth hardware:** `LedgerConfirmationModal`,
`LedgerConnect` (`Scan`, entry), and `ConnectHardware/SelectHardware`
now call `useAnalytics` from
`app/components/hooks/useAnalytics/useAnalytics` instead of
`useMetrics`.
- **QR hardware:** `ConnectQRHardware` (container + `Instruction`),
`AnimatedQRScanner`, and `QRSigningDetails` use the same hook swap;
event names and `MetaMetricsEvents` / builder usage stay as before.
- **Wallet recovery (web3auth-owned views):** `RestoreWallet`,
`WalletRestored`, and `WalletResetNeeded` (+ tests) migrated to
`useAnalytics`.
- **Post-onboarding settings:**
`OnboardingSuccess/OnboardingGeneralSettings`,
`OnboardingSuccess/OnboardingSecuritySettings` (+ test) migrated.
- **Other touchpoints in scope:** `ExperienceEnhancerModal`, `NftGrid`
and `NftGridItemActionSheet` tests—aligned with the C1 slice that
includes onboarding-adjacent and shared UI still on legacy metrics.
- **Tests:** `jest.mock` targets point at `useAnalytics`; mocks use
`createMockUseAnalyticsHook` from `app/util/test/analyticsMock`. Where
builders were referenced from tests, imports use `AnalyticsEventBuilder`
from `app/util/analytics/AnalyticsEventBuilder` instead of
`MetricsEventBuilder`.

No change to which events fire or their payloads—this is an API
migration only, consistent with prior analytics refactors (e.g. [PR A2 —
split types from
MetaMetrics](#26988)).

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

Refs: #26686

## **Manual testing steps**

N/A — refactor-only; behavior and analytics events are unchanged. Unit
tests cover updated mocks and components.

## **Screenshots/Recordings**

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

### **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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.

<!-- Generated with the help of the pr-description AI skill -->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Mostly a hook/API swap, but it touches user-facing hardware wallet and
recovery/onboarding flows and can subtly change analytics payloads or
break tracking if the new hook differs from `useMetrics`. One NFT
tracking event (`WALLET_ADD_COLLECTIBLES`) also gains additional
properties, changing event data.
> 
> **Overview**
> Swaps analytics wiring across Ledger/QR hardware wallet UI, wallet
recovery views, and onboarding success settings to use `useAnalytics`
instead of `useMetrics`, including updating imports and call sites.
> 
> Updates unit tests to mock `useAnalytics` consistently via
`createMockUseAnalyticsHook`/`AnalyticsEventBuilder`, and adjusts one
NFT event (`MetaMetricsEvents.WALLET_ADD_COLLECTIBLES`) to include extra
properties in its payload.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
aee6674. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…y Button) (#27700)

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

Clarify the `@deprecated` notice on `ButtonLink` so migration paths are
explicit and unambiguous:
- Use `TextButton` for inline links within text flows
- Use `Button` with `variant={ButtonVariant.Tertiary}` for standalone
link‑style buttons (CTAs, headers, separators)

Adds concise inline examples and links to design‑system docs. No runtime
behavior changes (comment‑only).

Context: [Slack
thread](https://consensys.slack.com/archives/C0354T27M5M/p1773898792873879?thread_ts=1773898792.873879&cid=C0354T27M5M)

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

N/A (documentation‑only change)

## **Screenshots/Recordings**

### **Before**
N/A

### **After**
N/A

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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

[Slack
Thread](https://consensys.slack.com/archives/C0354T27M5M/p1773898792873879?thread_ts=1773898792.873879&cid=C0354T27M5M)

<div><a
href="https://cursor.com/agents/bc-25a23bc9-6961-5476-a358-35b83636d6c2"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-25a23bc9-6961-5476-a358-35b83636d6c2"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: George Marshall <georgewrmarshall@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**

This updates
[.github/scripts/collect-qa-stats.mjs](vscode-file://vscode-app/Applications/Cursor.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/.github/scripts/collect-qa-stats.mjs)
so QA stats include E2E MetaMetrics coverage in its own top-level
metametrics namespace (alongside unit, component_view, e2e, and
performance).

The script statically scans event display names that tests assert: it
walks tests/helpers/analytics/expectations/*.ts and an explicit
LEGACY_INLINE_METAMETRICS_PATHS list for specs/helpers that still use
inline checks (getEventsPayloads, event.event ===, etc.). It resolves
onboardingEvents.* using tests/helpers/analytics/helpers.ts. Emitted
keys are metametrics_events_checked_unique_count and
metametrics_events_checked_names_json (sorted JSON array of 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:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the QA metrics pipeline by adding a new `metametrics`
namespace and altering artifact sourcing (optional fixture mode), which
could affect downstream dashboards/alerts if parsing or artifact naming
assumptions break.
> 
> **Overview**
> Extends `.github/scripts/collect-qa-stats.mjs` to emit a new top-level
`metametrics` namespace in `qa-stats.json`, populated by a static scan
of E2E sources to count and list unique MetaMetrics event names asserted
in tests.
> 
> Adds an optional local fixture mode (`QA_STATS_FIXTURE_ROOT`) that
bypasses the GitHub API/token and reads artifacts from extracted
directories, and tweaks E2E logging/output to better handle runs without
JUnit artifacts and to print the JSON body after writing.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b371f36. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…27735)

## **Description**

This speeds up the withdraw token-selection path by moving the allowlist
filter earlier in the pipeline.

Before this change, withdraw flows loaded the full token catalog, built
`AssetType`s for all matching catalog entries, sorted the full result,
and only then filtered down to the small allowlisted set we actually
care about. This change adds a `tokenFilter` hook parameter so
`useAccountTokens` can skip irrelevant catalog entries before building
them, and `useWithdrawTokenFilter` passes the withdraw allowlist down to
that lower-level loop.

It also keeps the existing post-filter in place for correctness, so
allowlisted tokens already owned by the user and native-token address
mapping still work as before.

Local dev measurements while opening **Predict Withdraw**:

| Metric | Before | After |
| --- | ---: | ---: |
| `useAccountTokens` | 954-1,143ms | 41-48ms |
| Catalog tokens built | 18,524 | 0 |
| Total tokens sorted | 18,618 | 15 |

This brings the measured token-loading path from roughly `~1.05s` down
to `~44ms` (`~24x` faster).

## **Changelog**

CHANGELOG entry: Improved performance when loading Predict withdraw
token selection

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1054

## **Manual testing steps**
1. Open Predict Withdraw, it will load much faster now



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes how withdraw token lists are
constructed and filtered, which could inadvertently hide valid tokens if
chainId/address normalization or `assetId` handling is wrong. Scope is
limited to token-selection hooks and is covered by updated/added unit
tests.
> 
> **Overview**
> Speeds up Predict Withdraw token selection by pushing the withdraw
allowlist filter down into token collection, avoiding building/sorting
the full token catalog before narrowing to allowlisted entries.
> 
> This introduces an optional `tokenFilter(chainId, address)` plumbed
from `useWithdrawTokenFilter` → `useSendTokens` → `useAccountTokens`,
and replaces the previous post-filtering in `useWithdrawTokenFilter`
with a precomputed allowlist lookup that also maps zero-address entries
to chain-specific native token addresses.
> 
> Tests are updated to assert `useSendTokens` is called with the new
filtering options and to validate `tokenFilter` behavior
(case-insensitive matching, chain gating, and native-token mapping),
plus new `useAccountTokens` coverage ensuring both owned assets and
catalog token building respect `tokenFilter`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b8e1cc7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: dan437 <80175477+dan437@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 a new yarn test:e2e:ai-test-plan script that runs the
e2e-ai-analyzer in generate-test-plan mode.

Updates the e2e-ai-analyzer README with instructions and examples for
generating a release test plan (including passing --pr flags and
required API keys).

## **Changelog**

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

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

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

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **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 Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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**
> Low risk: adds a new npm script and documentation only, with no
production/runtime code changes.
> 
> **Overview**
> Adds a new `yarn test:e2e:ai-test-plan` script that runs the
`e2e-ai-analyzer` in `generate-test-plan` mode.
> 
> Updates the `e2e-ai-analyzer` README with instructions and examples
for generating a release test plan (including passing `--pr` flags and
required API keys).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
57e1660. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This PR updates the change log for 7.72.0. (Hotfix - no test plan
generated.)

---------

Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
Co-authored-by: chloeYue <chloe.gao@consensys.net>
#28458)

## Summary
Cherry-picks [PR
#28424](#28424)
(`chore(deps): bump @xmldom/xmldom to 0.8.12` + lockfile dedupe) onto
`release/7.72.0` for inclusion in the release train tracked by [PR
#27990](#27990).

## Motivation
Addresses production dependency audit (`GHSA-wh4c-j3r5-mjhp`) by bumping
`@xmldom/xmldom` to `0.8.12` and deduping `yarn.lock` so resolved
versions align.

## Commits (cherry-picked)
- `46057e87e4` — chore(deps): bump @xmldom/xmldom to 0.8.12
- `776772ffcd` — chore(deps): dedupe lockfile after xmldom bump
CHANGELOG entry: null

## Testing
- [ ] CI green on this PR
- [ ] `yarn audit:ci` / audit expectations as appropriate for release
branch

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk dependency-only change: bumps `@xmldom/xmldom` and updates
the lockfile resolution/checksum, with no application code changes.
> 
> **Overview**
> Updates the `@xmldom/xmldom` dependency from `^0.8.10` to `^0.8.12` in
`package.json`.
> 
> Refreshes `yarn.lock` to resolve `@xmldom/xmldom` to `0.8.12` (updated
resolution key/checksum), aligning the lockfile with the bumped version.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
307083e. 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: georgewrmarshall <george.marshall@consensys.net>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - draft PR

All E2E tests pre-selected.

View GitHub Actions results

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

E2E Fixture Validation — Schema is up to date
17 value mismatches detected (expected — fixture represents an existing user).
View details

@chloeYue
Copy link
Copy Markdown
Contributor

chloeYue commented Apr 7, 2026

@SocketSecurity ignore-all

@chloeYue chloeYue marked this pull request as ready for review April 7, 2026 17:00
@chloeYue chloeYue requested review from a team as code owners April 7, 2026 17:00
@chloeYue chloeYue merged commit 470307e into stable Apr 7, 2026
945 of 989 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.