Skip to content

[pull] main from MetaMask:main#450

Merged
pull[bot] merged 8 commits into
Reality2byte:mainfrom
MetaMask:main
Jan 13, 2026
Merged

[pull] main from MetaMask:main#450
pull[bot] merged 8 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Jan 13, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

matallui and others added 8 commits January 13, 2026 17:51
## **Description**

This PR adds React hooks for subscribing to real-time WebSocket updates
in the Predict feature. The hooks integrate with the existing
`PredictController` and `WebSocketManager` infrastructure to provide
live game updates and market price feeds.

**Why:**
- Enable real-time UI updates for NFL games (scores, periods, game
status)
- Provide live market price updates (best bid/ask) without polling
- Support the upcoming live NFL game experience

**What's included:**
- `useLiveGameUpdates` - Hook for subscribing to real-time game state
changes
- `useLiveMarketPrices` - Hook for subscribing to market price updates
with Map-based state
- Controller methods to bridge UI hooks with provider WebSocket
subscriptions
- Comprehensive unit tests (33 tests, 100% passing)

## **Changelog**

CHANGELOG entry: null

## **Related issues**

[PRED-462](https://consensyssoftware.atlassian.net/browse/PRED-462)

## **Manual testing steps**

```gherkin
Feature: Live game updates subscription

  Scenario: User views live game data
    Given user is on a market details page for an active NFL game
    And WebSocket connection is established

    When game state changes (score update, period change)
    Then UI receives real-time update via useLiveGameUpdates hook
    And game information displays updated values

  Scenario: User views live market prices
    Given user is viewing market outcomes with token IDs
    And WebSocket connection is established

    When market prices change
    Then UI receives real-time price update via useLiveMarketPrices hook
    And prices map contains latest bid/ask values
```

## **Screenshots/Recordings**

### **Before**

N/A - New feature (hooks only, no UI changes)

### **After**

N/A - New feature (hooks only, no UI changes)

## **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]
> Enables real-time data flows in Predict via controller/provider APIs
and React hooks.
> 
> - Adds `useLiveGameUpdates` and `useLiveMarketPrices` hooks for
subscribing to game state and price updates, with connection polling,
cleanup, and state (lastUpdateTime, Map-backed prices)
> - Extends `PredictController` with `subscribeToGameUpdates`,
`subscribeToMarketPrices`, and `getConnectionStatus` (no-op/disconnected
fallbacks when unsupported)
> - Updates `PolymarketProvider` to delegate to `WebSocketManager`
(`subscribeToGame`, `subscribeToMarketPrices`, `getConnectionStatus`)
and map status to `ConnectionStatus`
> - Enhances provider `types` with `GameUpdateCallback`,
`PriceUpdateCallback`, and `ConnectionStatus`; exports hooks in
`hooks/index.ts`
> - Adds thorough unit tests for hooks, controller WebSocket methods,
and provider delegation
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
23776cb. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

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

## **Description**

Update the browser button styling in TrendingView to match the
ExploreSearchBar for visual consistency.

<img width="1760" height="842" alt="image"
src="https://github.com/user-attachments/assets/2981ed08-2e2a-49b7-b0b9-20721e7c91b4"
/>


## **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: style: explore tab button styling

## **Related issues**

Fixes: #24344
https://consensyssoftware.atlassian.net/browse/ASSETS-2240

## **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="389" height="113" alt="Screenshot 2026-01-13 at 15 14 39"
src="https://github.com/user-attachments/assets/71a74159-a42b-48f7-8cfa-c73211bbb97e"
/>

<img width="375" height="98" alt="Screenshot 2026-01-13 at 15 14 47"
src="https://github.com/user-attachments/assets/e58b2960-8da8-48b9-b898-da125e2cc51b"
/>

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

---
<a
href="https://cursor.com/background-agent?bcId=bc-ea04488d-8041-4b62-a2e4-339551a610af"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/open-in-cursor-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/open-in-cursor-light.svg"><img alt="Open in
Cursor"
src="https://cursor.com/open-in-cursor.svg"></picture></a>&nbsp;<a
href="https://cursor.com/agents?id=bc-ea04488d-8041-4b62-a2e4-339551a610af"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/open-in-web-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/open-in-web-light.svg"><img alt="Open in Web"
src="https://cursor.com/open-in-web.svg"></picture></a>



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Aligns the TrendingView browser button with `ExploreSearchBar` styling
for consistency.
> 
> - Wraps button content in a rounded, muted `Box` with `min-h/min-w`
(44px) instead of bordered square
> - Uses `TextVariant.BodyLg` with `TextColor.TextAlternative` for tab
count; otherwise `IconName.Explore` at `IconSize.Lg` with
`IconColor.IconAlternative`
> - Moves `testID` to the `TouchableOpacity`
(`trending-view-browser-button`)
> - Adds imports for `IconColor` and `TextColor`
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
67b8957. 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: Prithpal Sooriya <prithpal.sooriya@consensys.net>
## **Description**

This PR updates the header and footer styling across account-related
screens to align with the new design system patterns. The changes
migrate multiple screens from legacy navigation headers to the new
`HeaderWithTitleLeft` and `HeaderCenter` components from
`component-library/components-temp`.

**Screens updated:**
- **AccountSelector** - Now uses `HeaderCenter` for consistent bottom
sheet header styling with a close button
- **ImportNewSecretRecoveryPhrase** - Migrated to `HeaderWithTitleLeft`
with proper back/scan button props
- **SelectHardware (ConnectHardware)** - Migrated to
`HeaderWithTitleLeft` with subtitle support
- **ImportPrivateKey** - Migrated to `HeaderWithTitleLeft` and replaced
raw `TextInput` with `TextField` component
- **SeedphraseModal** - Added `HeaderCenter` component and updated
button variant from Primary to Secondary

**Additional improvements:**
- Standardized platform-specific padding for footers (16px on iOS, 0 on
Android)
- Updated text colors to use `TextColor.Alternative` for
subtitle/description text
- Cleaned up unused style properties and imports
- Improved hardware wallet button layout with proper flex styling

## **Changelog**

CHANGELOG entry: Updated header and footer styling across account
management screens to align with new design system

## **Related issues**

Fixes:
https://consensyssoftware.atlassian.net/jira/software/c/projects/MDP/boards/2972?assignee=62afb43d33a882e2be47c36f&quickFilter=3325&selectedIssue=MDP-322

https://consensyssoftware.atlassian.net/jira/software/c/projects/MDP/boards/2972?assignee=62afb43d33a882e2be47c36f&quickFilter=3325&selectedIssue=MDP-324

## **Manual testing steps**

```gherkin
Feature: Account flow header styling

  Scenario: User views account selector
    Given the user is on the wallet home screen
    When user taps on the account selector
    Then the bottom sheet should display with a centered header and close button

  Scenario: User imports a wallet with SRP
    Given the user is on the add account flow
    When user selects "Import wallet"
    Then the import SRP screen should display the new left-aligned header with back and scan buttons

  Scenario: User imports a private key
    Given the user is on the add account flow
    When user selects "Import account" (private key)
    Then the import screen should display with the new left-aligned header
    And the text input should display with the updated TextField component styling

  Scenario: User connects hardware wallet
    Given the user is on the add account flow
    When user selects "Add hardware wallet"
    Then the select hardware screen should display with the new left-aligned header and subtitle

  Scenario: User views seedphrase modal
    Given the user is on the backup seedphrase flow
    When user taps on "What is a Secret Recovery Phrase?"
    Then the modal should display with centered header and close button
    And the "Got it" button should be styled as secondary variant
```

## **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/dd34213c-42a6-4779-8fd1-15dfe2d84616

<!-- [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]
> Aligns account-related screens with the new design system headers and
spacing.
> 
> - Replaces legacy nav headers with
`HeaderWithTitleLeft`/`HeaderCenter` in `ImportNewSecretRecoveryPhrase`,
`ImportPrivateKey`, `SelectHardware`, `AccountSelector`, and
`SeedphraseModal`
> - Standardizes footer/content spacing: `paddingBottom`
iOS=16/Android=0; adds `paddingHorizontal`/`paddingTop` where applicable
> - Updates text styling to `TextColor.Alternative` for descriptions and
refines list/bullet spacing
> - Changes `SeedphraseModal` CTA to `ButtonVariants.Secondary`; keeps
SRP import CTA primary and moves it to sticky footer area
> - Refactors `ImportPrivateKey`: new header with inline learn-more
link, updated placeholder, disabled state while loading, and refreshed
input/typography styles
> - Cleans up unused styles/imports and updates tests/snapshots and e2e
selectors to match new headers and layout
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1f1b86b. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

This PR adds the ability to populate team information (name, logo,
abbreviation, colors) into the `market.game` object for live sports
markets.

**Why:** Live sports UI needs team data (logos, colors, names) to render
game cards and details pages. Previously, the `market.game` object
existed but had no team information populated.

**Solution:**
- **TeamsCache**: Singleton that fetches and caches team data from the
Polymarket Gamma API (`/teams?league={league}`), keyed by league and
team abbreviation
- **gameParser utilities**: Functions to extract league from event tags,
parse game slugs for team abbreviations, and build complete game data
with team objects
- **Integration**: `PolymarketProvider.getMarkets()` and
`getMarketDetails()` now load team data and populate `market.game` with
home/away team information

The implementation is designed to be league-agnostic via a
`LIVE_SPORTS_LEAGUES` constant - adding a new league just requires
adding it to that array.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-463

## **Manual testing steps**

```gherkin
Feature: Team information in live sports markets

  Scenario: user views sports markets with team data
    Given user has the Predict feature enabled
    And there are active NFL game markets

    When user navigates to the Sports category
    Then markets display with game data including team names, logos, and colors

  Scenario: user views market details for a sports game
    Given user has the Predict feature enabled
    And there is an active NFL game market

    When user taps on the market to view details
    Then the market details include home and away team information
    And team logos and colors are available for rendering
```

## **Screenshots/Recordings**

### **Before**

N/A - This is backend/data layer change, no UI changes in this PR.

### **After**

N/A - This is backend/data layer change, no UI changes in this PR.

## **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]
> Introduces live sports team and game data plumbing with conditional
enablement and extensive tests.
> 
> - Adds `LIVE_SPORTS_LEAGUES` and `isLiveSportsEnabled` flag to gate
live sports features
> - New `TeamsCache` singleton fetches/caches teams from Gamma API and
exposes lookups (with comprehensive tests)
> - New `gameParser` utilities to detect league, parse slugs, build
`game` data, format period, and parse scores (tests included)
> - Updates types: `PredictMarketGame.score` becomes structured `{ away,
home, raw }`; `elapsed`/`period` are nullable; Polymarket API event type
extended with game fields
> - `GameCache` overlays parsed scores and null-safe clock/period; tests
updated accordingly
> - `parsePolymarketEvents` now accepts options including `teamLookup`;
builds `game` when available; `getParsedMarketsFromPolymarketApi`
threads `teamLookup`
> - `PolymarketProvider.getMarkets`/`getMarketDetails` conditionally
load leagues via `TeamsCache`, pass `teamLookup`, and only overlay
`GameCache` when live sports enabled (tests cover enabled/disabled
paths)
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2896cb7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

When navigating to swaps from a trending token:
1. The source token is set to the native gas token of the same chain as
the trending token
2. The destination token is set as the trending token

If the trending token happens to be the native gas token:
1. Source: default dest token for that chain (usually a stablecoin like
USDC, USDT)
2. Dest: native gas token

This provides a better UX since users can immediately swap using the
native token of that chain.

You will need to force `selectAssetsTrendingTokensEnabled` to return
`true` to test this feature for now.

## **Changelog**

CHANGELOG entry: Set trending token as the dest token when navigating to
swaps

## **Related issues**

Fixes: SWAPS-3688

## **Manual testing steps**

```gherkin
Feature: Trending token swap navigation

  Scenario: user taps swap from a trending token on network
    Given user is viewing a trending token on network

    When user taps the Swap button
    Then the swap source token should be native token
    And the swap destination token should be the trending token
```

## **Screenshots/Recordings**

### **Before**

<!-- Source token was always ETH on mainnet -->

### **After**

<!-- Source token is now native token of the trending token's chain -->



https://github.com/user-attachments/assets/4a87dfae-031b-4a91-89a9-225fda808c5f



## **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]
> Improves swap/bridge prefill logic and integrates Trending tokens
intent.
> 
> - Add `getSwapTokens` in `AssetOverview` to compute
`sourceToken`/`destToken` based on context: native gas tokens use
default pair → native; trending tokens use native → asset; otherwise
asset → (unspecified)
> - Pass computed tokens to `useSwapBridgeNavigation` and export
`isAssetFromTrending`; leverage `isNativeAddress`,
`getNativeSourceToken`, and `getDefaultDestToken`
> - Mark Trending navigation with `isFromTrending` in
`TrendingTokenRowItem` so Asset view enters buy mode; update tests
accordingly
> - Extend tests: capture hook args, add cases for
trending/non-trending, cross-chain, native token paths, and unit tests
for `getSwapTokens`
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3e695bd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…2] (#22076)

# Pull Request Description

## **Description**

This PR migrates MetaMask Mobile from the legacy MetaMetrics system to
the new `@metamask/analytics-controller` package. This migration
standardizes analytics across MetaMask products and provides a more
maintainable, controller-based architecture.

**Reason for change:**
- Standardize analytics implementation across MetaMask products using
the shared `@metamask/analytics-controller` package
- Improve maintainability by using a controller-based architecture that
integrates with the Engine
- Reduce code duplication and improve type safety with shared analytics
types
- Prepare for future deprecation of the legacy MetaMetrics system

**Improvement/Solution:**
- Integrated `@metamask/analytics-controller` as a new Engine controller
- Created migration 110 to migrate legacy storage keys (METAMETRICS_ID,
MIXPANEL_METAMETRICS_ID) to the new ANALYTICS_ID key
- Migrated opt-in preference from METRICS_OPT_IN to AnalyticsController
state
- Created new analytics utility module
(`app/util/analytics/analytics.ts`) that provides a clean API for
components
- Updated all controller initializations to use `analyticsId` instead of
`metaMetricsId`
- Added AnalyticsController messenger delegation to controllers that
need to track events
- Maintained backward compatibility by keeping MetaMetrics as a wrapper
around the new system
- Updated all test files with appropriate mocks for the new analytics
system

> [!NOTE]
> ## Why This PR Was Not Split
>
> ### Core Reasons
>
> - **Segment SDK Conflict**: Both MetaMetrics and analytics-controller
would instantiate Segment SDK simultaneously, causing conflicts. They
must be coordinated atomically
> - **Breaking API Change**: `Engine.init()` signature changed from
`init({})` to `init(analyticsId: string)` (where `analyticsId` is a UUID
string), requiring updates to all test mocks and controller
initialization
> - **Controller Messenger Updates**: Controllers using MetaMetrics
instance methods now call analytics via messenger, requiring init
messenger updates across 9+ controllers
> - **State Migration**: Migration 110 migrates legacy analytics storage
to new AnalyticsController state (depends on full integration)
> - **Test Infrastructure**: Extensive test mock updates needed
(MetaMetrics singleton → messenger pattern, Engine.init() signature
change, AnalyticsController state changes)
> 
> ### Why Atomic
> 
> - Cannot partially migrate due to Segment SDK conflict
> - Breaking changes cascade — splitting would leave codebase in broken
intermediate state
> - State migration depends on AnalyticsController being present in
Engine
> - Messenger pattern requires full setup to be in place
> - Test mocks must be updated together to prevent flakiness


### **Technical Details**

#### Key Changes

1. **New AnalyticsController Integration**
   - Added `@metamask/analytics-controller` 1.0.0 package dependency
   - Created `analytics-controller-init.ts` to initialize the controller
   - Created `analytics-controller-messenger.ts` for messenger setup
- Created `platform-adapter.ts` to bridge AnalyticsController with
Segment SDK
- Added `'AnalyticsController:stateChange'` to
`BACKGROUND_STATE_CHANGE_EVENT_NAMES` for state synchronization

2. **Storage Migration (Migration 110)**
- Migrates `METAMETRICS_ID` and `MIXPANEL_METAMETRICS_ID` to
`ANALYTICS_ID`
   - Migrates `METRICS_OPT_IN` to `AnalyticsController.optedIn` state
   - Validates UUIDv4 format for analytics IDs
   - Deletes deprecated `MIXPANEL_METAMETRICS_ID` key

3. **New Analytics Utility Module**
- Created `app/util/analytics/analytics.ts` as the main API for
components
   - Created `app/util/analytics/analyticsId.ts` for ID management
- Created `app/util/analytics/AnalyticsEventBuilder.ts` for event
construction
- Created `app/util/analytics/queue.ts` for event queuing before Engine
is ready
- Updated `generateDeviceAnalyticsMetaData()` to return
`AnalyticsUserTraits` type for type safety

4. **Controller Updates**
- Updated all controller init functions to use `analyticsId` parameter
   - Added AnalyticsController messenger delegation to:
     - TokenDetectionController
     - TransactionController
     - NetworkController
     - DeFiPositionsController
     - AccountTreeController
     - SnapController
     - BridgeController
     - UserStorageController

5. **Component Updates**
- Updated components to use new `analytics` utility instead of
`MetaMetrics.getInstance()`
   - Updated `useMetrics` hook to use new analytics system
   - Added analytics mocks to test files
   - Added `updateDataRecordingFlag` mocks to all MetaMetrics test mocks
- Updated test mocks to use `Engine.init('')` with string parameter
instead of `Engine.init({})`
- Added `whenEngineReady` mocks to prevent Engine access after Jest
teardown
- Fixed `Linking.openUrl` typo to `Linking.openURL` in
LendingLearnMoreModal tests

6. **Backward Compatibility**
   - MetaMetrics and useMetrics marked as deprecated
   - MetaMetrics class now wraps the new analytics system
- Added `updateDataRecordingFlag()` method to MetaMetrics interface for
data deletion workflows
   - Legacy storage keys are preserved for backward compatibility
   - Existing event tracking continues to work

7. **E2E Test Updates**
- Updated `FixtureBuilder.withMetaMetricsOptIn()` to also set up
AnalyticsController state
- Added comment clarification in analytics helpers about catch-all proxy
handler
- Updated test utilities to use `analyticsId` instead of `metaMetricsId`

#### Files Changed

- **New Files:**
-
`app/core/Engine/controllers/analytics-controller/analytics-controller-init.ts`
- `app/core/Engine/controllers/analytics-controller/platform-adapter.ts`
  - `app/core/Engine/messengers/analytics-controller-messenger.ts`
  - `app/util/analytics/analytics.ts`
  - `app/util/analytics/analyticsId.ts`
  - `app/util/analytics/AnalyticsEventBuilder.ts`
  - `app/util/analytics/queue.ts`
  - `app/util/analytics/analytics.types.ts`
  - `app/store/migrations/110.ts`
  - `app/core/Analytics/whenEngineReady.ts`
  - `app/selectors/analyticsController.ts`

- **Modified Files:**
  - All controller initialization files updated to use `analyticsId`
  - All messenger files updated to delegate AnalyticsController actions
- All test files updated with analytics mocks and
`updateDataRecordingFlag` mocks
  - Engine.ts updated to initialize AnalyticsController
- `app/core/Engine/constants.ts` - Added AnalyticsController to
background state change events
- `app/core/Engine/utils/test-utils.ts` - Updated to use `analyticsId`
parameter
- `e2e/framework/fixtures/FixtureBuilder.ts` - Updated to set
AnalyticsController state
  - Various utility files migrated to use new analytics API

## **Changelog**

CHANGELOG entry: null

*(This is an internal refactoring that does not change user-facing
functionality)*

## **Related issues**

Fixes: #22017

## **Manual testing steps**

```gherkin
Feature: Analytics tracking with AnalyticsController

  Scenario: User opts in to analytics
    Given the app is installed with legacy analytics storage (METAMETRICS_ID, METRICS_OPT_IN)
    
    When the app starts and migration 110 runs
    Then the legacy analytics ID should be migrated to ANALYTICS_ID
    And the opt-in preference should be migrated to AnalyticsController state
    And analytics events should continue to track correctly

  Scenario: User tracks events after migration
    Given the user has opted in to analytics
    
    When the user performs actions that trigger analytics events (e.g., sends transaction, views dapp)
    Then events should be tracked through AnalyticsController
    And events should be sent to Segment via the platform adapter

  Scenario: User changes analytics preference
    Given the user is on the Security Settings screen
    
    When the user toggles the analytics opt-in/opt-out setting
    Then the AnalyticsController state should update
    And the preference should persist across app restarts
```

## **Screenshots/Recordings**

### **Before**

<img width="2992" height="2676" alt="image"
src="https://github.com/user-attachments/assets/e8cab114-8324-4da0-9bf8-a191091ed2d2"
/>

### **After**

same, no changes

## **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]
> Modernizes analytics plumbing and aligns with AnalyticsController
while keeping legacy compatibility.
> 
> - Refactors `MetaMetrics` to delegate to new `analytics` utility (adds
`updateDataRecordingFlag`, deprecates group/reset/flush) and updates
tracking flow
> - Migrates components/hooks to `analytics` and `AnalyticsEventBuilder`
(`useMetrics`, `AppStateEventListener`,
`MetaMetricsAndDataCollectionSection`, `ThemeSettings`)
> - Adds `core/Analytics/whenEngineReady` helper and uses it in tests;
fixes `Linking.openURL` in tests
> - Updates storage constants: introduces `ANALYTICS_ID`, marks legacy
keys deprecated, notes migrations
> - Test infra overhaul: mocks `analytics` and
`updateDataRecordingFlag`, switches `Engine.init('')`, adds
navigation/metrics mocks, increases debounce timeouts and wraps
`fireEvent` in `act`
> - Minor behavioral tweaks: send theme as user trait; preserve data
deletion workflows
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b6839ad. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**
- remove npx 
- add another input field for eas channel to push updates to 

## **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**
Fingerprint is working after the changes

<img width="1055" height="302" alt="Screenshot 2026-01-07 at 6 10 59 AM"
src="https://github.com/user-attachments/assets/a8bfea40-3853-4e6f-ae16-76a9d51ab56c"
/>

## **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]
> Modernizes the OTA workflow and build script to support multiple
release channels and cleaner CI.
> 
> - Adds `channel` input (exp/rc/production) and maps to environments;
build step now dispatches to `build:expo-update:main:{exp|rc|prod}`
accordingly
> - Simplifies fingerprinting by removing detailed `npx
@expo/fingerprint` diffs and keeping a summary-only compare/output
> - Updates env/secrets for OTA job: replaces QA-specific Segment vars
with generic keys, adds Google client IDs, FCM config, QuickNode URLs,
Branch test key, and standardizes Sentry vars
> - In `scripts/build.sh`, skips env remapping when `PLATFORM` is
`expo-update` to avoid overriding externally provided channel/config
> - Renames workflow and minor logging/summary tweaks
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3fab301. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com>
Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
Co-authored-by: tommasini <tommasini15@gmail.com>
…re flag (#24494)

## **Description**

Replaces the hardcoded `LIVE_SPORTS_LEAGUES` configuration with a remote
feature flag (`predictLiveSports`), allowing live sports functionality
to be enabled/disabled and leagues to be configured remotely without app
updates.

**Motivation**: The live sports feature needs to be controllable via
remote config to enable gradual rollout and quick disabling if issues
arise.

**Solution**:
- Added `PredictLiveSportsFlag` type: `{ enabled: boolean; leagues:
string[] }`
- Added `DEFAULT_LIVE_SPORTS_FLAG` constant (disabled by default, empty
leagues)
- Renamed `LIVE_SPORTS_LEAGUES` to `SUPPORTED_SPORTS_LEAGUES` (defines
what the app supports)
- Added `filterSupportedLeagues()` helper to validate remote config
against supported leagues
- Updated `PredictController.getMarkets()` and `getMarket()` to read the
flag from `RemoteFeatureFlagController`
- Updated provider interfaces to accept `liveSportsLeagues` parameter
- Removed the now-unused `selectPredictLiveNflEnabled` selector and
`isLiveSportsEnabled()` function

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-464

## **Manual testing steps**

```gherkin
Feature: Live Sports Remote Feature Flag

  Scenario: Live sports disabled by default
    Given the predictLiveSports flag is not set in remote config
    When user views prediction markets
    Then live sports markets are not fetched

  Scenario: Live sports enabled via remote config
    Given the predictLiveSports flag is { enabled: true, leagues: ["nfl"] }
    When user views prediction markets
    Then NFL live sports markets are included in results

  Scenario: Unsupported league filtered out
    Given the predictLiveSports flag is { enabled: true, leagues: ["nfl", "mlb"] }
    When user views prediction markets
    Then only NFL markets are fetched (mlb not in SUPPORTED_SPORTS_LEAGUES)
```

## **Screenshots/Recordings**

### **Before**

N/A - Internal refactor, no UI changes

### **After**

N/A - Internal refactor, no UI changes

## **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]
> Enables remotely configurable live sports by validating and passing
leagues to market fetches.
> 
> - Add `PredictLiveSportsFlag` and `DEFAULT_LIVE_SPORTS_FLAG`;
introduce `SUPPORTED_SPORTS_LEAGUES` and `filterSupportedLeagues` to
validate remote config
> - `PredictController.getMarkets()`/`getMarket()` read
`RemoteFeatureFlagController.predictLiveSports`, compute
`liveSportsLeagues`, and pass to providers
> - Update provider types/interfaces to accept `liveSportsLeagues`;
`PolymarketProvider` uses provided leagues (removes
`isLiveSportsEnabled` and `LIVE_SPORTS_LEAGUES`), loads `TeamsCache`
conditionally, and overlays game data only when leagues provided
> - Remove `selectPredictLiveNflEnabled` selector and related tests;
refresh tests across controller/provider to cover enabled/disabled and
undefined leagues paths
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
68f8e7d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators Jan 13, 2026
@pull pull Bot added the ⤵️ pull label Jan 13, 2026
@pull pull Bot merged commit 1e4728a into Reality2byte:main Jan 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants