Skip to content

[pull] main from MetaMask:main#775

Merged
pull[bot] merged 12 commits into
Reality2byte:mainfrom
MetaMask:main
May 20, 2026
Merged

[pull] main from MetaMask:main#775
pull[bot] merged 12 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 20, 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 : )

juanmigdr and others added 12 commits May 20, 2026 18:51
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **Description**

**Root cause:** The Polymarket API returns `null` for the
`orderPriceMinTickSize` field on some sports markets. Our code was
calling `.toString()` on that value without a null check, which threw a
runtime error. Since the entire batch of events was parsed in a single
`.map()`, one bad event crashed the whole thing and returned an empty
list.

**Fix:**
1. Changed `market.orderPriceMinTickSize.toString()` to
`market.orderPriceMinTickSize?.toString() ?? '0.01'` to safely handle
the null.
2. Updated the TypeScript type to `number | null` to correctly reflect
what the API actually returns.
3. Changed the event parsing loop from `.map()` to `.flatMap()` with a
per-event `try/catch`, so a single malformed event is skipped and logged
instead of taking down the whole batch.

<!--
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: football prediction markets not loading

## **Related issues**

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

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


https://github.com/user-attachments/assets/3fb77be4-1060-45c3-adb2-cb75cd4f41a8


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

### **After**


https://github.com/user-attachments/assets/464094d7-94ea-4002-8531-498bb6d173da


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

## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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 market parsing defaults and error handling for Polymarket
events; while localized, it can affect tick-size-dependent pricing
behavior and which events appear if parsing failures occur.
> 
> **Overview**
> Fixes Polymarket sports markets failing to load when the API returns a
`null` `orderPriceMinTickSize`.
> 
> Updates the Polymarket market type to allow `orderPriceMinTickSize:
number | null`, safely derives `tickSize` with a fallback default, and
makes `parsePolymarketEvents` resilient by parsing each event in a
`try/catch` (logging and skipping only the bad event instead of failing
the whole batch).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1f8de4a. 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: Luis Taniça <matallui@gmail.com>
…30452)

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

**Reason:** Users with a MetaMask Card and a Money Account need clearer
paths to link spending to their Money balance, copy that matches design
(including mUSD cashback and flexible APY), and less noisy linkage
feedback. Card Home previously lacked a dedicated entry point; the link
confirmation sheet used generic art and older copy.

**Solution:**

- **Card Home:** When `useMoneyAccountCardLinkage` reports `canLink`,
render a `MoneyMetaMaskCard` in `link` mode between dividers, with
`hideCardImage` so the hero card image is not duplicated, `apy` from
`useMoneyAccountBalance`, and metal vs virtual cashback via `CardType`.
Tapping the header, subtitle row, or **Link card** calls
`startLinkFlow({ screen: Routes.CARD.HOME })`. CTA stays visible while
`cardHomeDataStatus` is `loading` (stale-while-revalidate) when other
conditions still allow linking.
- **MoneyMetaMaskCard (link mode):** Support `hideCardImage` (vertical
bullets only), and when `apy` is `undefined`, use `link_subtitle_no_apy`
and omit the APY bullet instead of forcing `0%`.
- **MoneyLinkCardSheet:** Replace the mUSD coin with the user’s card art
from `selectCardHomeData` (`mm_card_metal` for `CardType.METAL`, else
`mm_card_regular`), Figma-aligned sizing, updated sheet description plus
`link_card_sheet_description_no_apy` when vault APY is not yet
available.
- **useMoneyAccountCardLinkage:** Single-line pending/success toasts,
updated error copy, pending spinner color `IconDefault`, and error icon
`Error` instead of `Danger`.
- **Spending Limit (from #30320):** Money Account preselection, Spend
and Earn promo UI (`SpendAndEarnPromoCard`, `ShimmerOverlay`), and
related `useSpendingLimit` / view tests.
- **Other (same branch vs `main`):** Bridge insufficient-native reserve
handling and tests; send confirmation alert modal and fee / percentage
amount hook adjustments with tests.

## **Changelog**

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

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

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

CHANGELOG entry: Added a Link Money Account section on Card Home,
redesigned the link-card bottom sheet (card art and copy), improved
Money Account card-linkage toasts, extended Spending Limit with Spend
and Earn / Money preselection, and tightened bridge and send
confirmation edge cases.

## **Related issues**

Fixes:

Refs: #30320

## **Manual testing steps**

```gherkin
Feature: Money Account linking and related card flows

  Scenario: Card Home shows link CTA when user can link
    Given I am a cardholder with Money Account requirements met, card home data loaded, and I am not already delegated for card spending
    When I open Card Home
    Then I see the Link MetaMask Card block with dividers and Link card action

  Scenario: Link card opens confirmation sheet with correct art and copy
    Given I tap Link card or the link header from Card Home (or open the link sheet from the Money link flow)
    When the Spend and earn bottom sheet appears
    Then I see the title Spend and earn, description mentioning mUSD back and up to my vault APY when available, and the card image matches my card type (metal vs virtual)

  Scenario: Confirm link still runs linkage after sheet dismiss
    Given the link confirmation sheet is open
    When I tap Link card on the sheet
    Then the sheet closes and the in-app linkage flow continues (pending then success or error toast)

  Scenario: No APY yet uses shorter description
    Given vault APY is not yet available to the client
    When I open the link confirmation sheet
    Then the body copy does not include an APY clause and no raw i18n placeholders appear

  Scenario: Spending Limit Spend and Earn promo (Refs #30320)
    Given I open Spending Limit under conditions where the Spend and Earn promo applies
    When I review the screen
    Then I see the promo treatment and Money Account is preselected where configured
```

## **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/67079894-786e-4806-a6eb-fa4dd8ee4b3d

## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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**
> Moderate risk: introduces new Money Account linking CTA paths and
conditional rendering on Card Home, plus updates toast/copy logic that
could affect linkage UX and user navigation if conditions are wrong.
> 
> **Overview**
> Adds a **Money Account linking entry point on Card Home**: when
`useMoneyAccountCardLinkage().canLink` is true, Card Home renders a
`MoneyMetaMaskCard` “link” section (with dividers), passes live `apy`
from `useMoneyAccountBalance`, detects metal vs virtual via `CardType`,
and routes all presses to `startLinkFlow({ screen: Routes.CARD.HOME })`.
> 
> Refines the **linking UX and copy**: `MoneyMetaMaskCard` now supports
`hideCardImage` (Card Home variant) and switches to *no-APY*
subtitle/bullets when APY is unavailable; `MoneyLinkCardSheet` swaps the
illustration to card art based on `selectCardHomeData` and uses no-APY
description when needed. Updates linkage toasts in
`useMoneyAccountCardLinkage` (single-line pending/success, new error
copy/icon/spinner color) and refreshes/enhances tests and i18n strings
accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3128d05. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **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?
-->
Removes the Feature Announcements notification setting by deleting
FeatureAnnouncementToggle (and its test) and dropping the corresponding
useFeatureAnnouncementToggle hook.

Replaced by AUS notification preference in
#30106

## **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: https://consensyssoftware.atlassian.net/browse/GE-244

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

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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 removal of unused UI toggle and associated hook/tests;
primary risk is any missed call sites leading to missing functionality,
but searches indicate no remaining references.
> 
> **Overview**
> Removes the **Feature Announcements** notification setting by deleting
`FeatureAnnouncementToggle` (and its test) and dropping the
corresponding `useFeatureAnnouncementToggle` hook.
> 
> Cleans up `useSwitchNotifications` and its tests by removing the
feature-announcement toggle flow (action/selector usage and list-refresh
behavior) so only the remaining notification/account toggle hooks are
exercised.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1b5ee03. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Move the feature-flag check from AssetPollingProvider into each polling
hook so polling no-ops with empty input when unified assets state is
off, while keeping hook call order stable for the provider.

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **Description**

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

## **Changelog**

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

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

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

CHANGELOG entry: gate asset controller polling on unified assets in
hooks

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

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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 when asset-related controllers start/stop polling by moving
unified-assets feature-flag gating into each polling hook; risk is
moderate because it affects background polling behavior and could change
network/load characteristics if the flag state is misinterpreted.
> 
> **Overview**
> Moves unified-assets feature-flag gating from `AssetPollingProvider`
into each asset polling hook (`useCurrencyRatePolling`,
`useTokenRatesPolling`, `useTokenDetectionPolling`,
`useTokenBalancesPolling`, `useMultichainAssetsRatePolling`) by
resolving normal inputs and then passing an empty `input` to
`usePolling` when the flag is enabled.
> 
> Updates `AssetPollingProvider` to always mount all polling hooks
(stable call order) and adjusts/adds tests to assert that polling does
**not** start when unified assets state is enabled, while provider tests
no longer cover the provider-level feature-flag branch.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ee35a16. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

This PR delivers two related pieces of work for the Predict crypto
up/down details screen:

1. **A reusable `usePredictOrderbook` hook** that streams live
Polymarket orderbook depth into the existing `LivelineChart` so the
chart shows real bid/ask activity overlaid on the price line. The hook
is plumbed through the existing controller / provider / WebSocket stack
— no new APIs needed at the engine layer.

   The data flow:

   ```mermaid
   flowchart LR
Hook["usePredictOrderbook(tokenId)"] -->
Controller["PredictController.subscribeToOrderbook"]
     Controller --> Provider["PolymarketProvider.subscribeToOrderbook"]
     Provider --> WSManager["WebSocketManager.subscribeToOrderbook"]
     Provider --> REST["getOrderBook (utils.ts)"]
     REST -->|"seed first snapshot"| WSManager
     WS["Polymarket market WS"] --> WSManager
     Hook --> Chart["LivelineChart orderbook prop"]
   ```

The WebSocket manager handles per-token reference counting, a 250 ms
emit throttle to protect the RN↔WebView bridge, `book` event ingestion,
and opportunistic top-of-book pruning on `price_change` events. The REST
bootstrap runs at the provider layer (where the active CLOB protocol is
known) and seeds the cache before the first WS event arrives.

2. **Chart UX/UI polish** to match the Figma reference for `BTC Up or
Down`:
- Removed the oversized green/red price pill (badge) — only the orange
dot remains.
- Removed the 36 px dead space above the chart (`padding.top: 48 → 12`).
- Made the chart line extend closer to the y-axis labels
(`padding.right: 80 → 64`).
- Pushed time-axis labels above the WebView's bottom clip zone so they
actually render (`padding.bottom: 48 → 80`).
- Dropped trailing `.00` from the y-axis price labels per design
(whole-dollar formatter).
- Added a compact 12-hour `h:mm:ss` `formatTime` so the time-axis labels
fit the live 30s window without overlap-culling.
- Extracted `CRYPTO_UP_DOWN_FORMAT_VALUE` and
`CRYPTO_UP_DOWN_FORMAT_TIME` as named exports so the WebView-bridged
formatter bodies can be regression-tested with `it.each`.

The PR also includes a self-review pass that fixed:
- Stale orderbook state surviving WebSocket reconnects (cleared
`orderbookState` + throttle timers in `disconnectMarket`).
- Dead `error` field on the orderbook hook's public type.
- Empty-string `tokenId` slipping past the subscribe guard.
- Misleading comment vs. implementation for `applyTopOfBook` (it prunes,
doesn't splice).
- Vague "with correct props" test name renamed per the repo's
unit-testing guidelines.
- Dead `createMockChartRef` helper + 19 unused `chartRef` locals + 2
orphaned `appendPoint` assertions in `useCryptoUpDownChartData.test.ts`
(the hook signature dropped its `chartRef` arg upstream).
- Magic-number documentation for chart padding and chart-height bounds.

### Deferred to follow-ups (intentional)
- `patch-package` on `liveline` for: (a) orderbook depth fade threshold
(currently fades only in the top 45% — design wants midpoint), (b)
time-axis edge-fade reduction, (c) palette opacity bumps. All require
modifying `node_modules/liveline/dist/index.js` and rebundling via `yarn
build:liveline-webview`; tracked separately because they coordinate with
chart-team work.
- Imperative `setOrderbook` on `LivelineChartRef` to skip a full
`SET_PROPS` round-trip on every depth tick (perf optimization that needs
upstream `liveline` cooperation).
- i18n keys for `Price to beat` / `Current price` / `Target` (no
`predict.crypto_up_down` namespace exists yet — broader Predict-feature
concern).

## **Changelog**

CHANGELOG entry: Added live orderbook depth visualization and refined
the chart layout for Crypto Up/Down markets.

## **Related issues**

Refs:

<!-- Fill in any internal ticket reference (e.g. Linear PRDT-NNNN or
GitHub issue) before publishing -->

## **Manual testing steps**

```gherkin
Feature: Crypto Up/Down details screen — live chart with orderbook depth

  Scenario: user opens a live Crypto Up/Down market and sees the redesigned chart
    Given the user has the Predict feature flag enabled
    And the user has navigated to a live "BTC Up or Down" market

    When the details screen finishes loading

    Then the chart line renders with the orange dot at the current price
    And no green or red price pill appears next to the dot
    And the y-axis price labels render to the right of the chart without trailing ".00"
    And there is no large empty band between the "Price to beat" header and the chart line
    And time labels in "h:mm:ss" format render along the bottom of the chart, above the action buttons
    And the dashed "Target" reference line renders at the target price

  Scenario: user observes live orderbook depth on the chart
    Given the user is on a live Crypto Up/Down market with active liquidity

    When the user watches the chart for a few seconds

    Then small green ($) labels for bids and red ($) labels for asks animate upward from the bottom-left of the chart
    And the labels update within ~250 ms of new orderbook events
    And on reconnect (e.g. toggle airplane mode briefly) the orderbook re-bootstraps without showing stale prices

  Scenario: user navigates away and the chart cleans up
    Given the chart is rendering with active orderbook depth

    When the user taps the back button

    Then the chart unmounts and the orderbook subscription is released
    And no further WebSocket traffic for that token is observed in the network log
```

## **Screenshots/Recordings**

### **Before**

<!-- Light-mode current chart (padding.bottom = 48, badge enabled, no
formatTime): time labels missing at bottom, large green price pill
obscures the dot, large empty band above the chart line, trailing ".00"
on y-axis labels. -->

### **After**

<!-- Dark-mode current chart: time labels (e.g. `2:51:20, 2:51:30`)
render above the action buttons, dot-only price marker, no top gap,
whole-dollar y-axis labels, live green/red orderbook depth flowing up
the left of the chart. -->


Uploading upDown.mov…



## **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
- `usePredictOrderbook.test.ts` (new) — subscribe/unsubscribe lifecycle,
snapshot mapping, mount/unmount safety, connection polling.
- `WebSocketManager.test.ts` — extended with positive coverage for
`book` events, REST seed, reference counting, reconnect, throttle,
opportunistic TOB prune.
- `PolymarketProvider.test.ts` — `subscribeToOrderbook` REST bootstrap
success/failure.
- `PredictController.test.ts` — passthrough +
`MESSENGER_EXPOSED_METHODS` registration.
- `PredictCryptoUpDownChart.test.tsx` — orderbook wiring, prop
forwarding, `CRYPTO_UP_DOWN_FORMAT_VALUE` and
`CRYPTO_UP_DOWN_FORMAT_TIME` `it.each` regression suites.
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- JSDoc on `usePredictOrderbook`, `CRYPTO_UP_DOWN_FORMAT_VALUE`,
`CRYPTO_UP_DOWN_FORMAT_TIME`, `CHART_HEIGHT_VIEWPORT_FRACTION`, and the
new WebSocketManager orderbook methods.
- [ ] 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.
- Apply at PR creation time: `team-predict` (or the appropriate Predict
team label), `area-predict`. Add `needs-qa` if the on-device cadence of
orderbook depth needs review.

#### Performance checks (if applicable)

- [x] I've tested on Android
- Pending — primary development was iOS; should be smoke-tested on an
Android mid-range device because the WebView bridge cadence differs.
- [x] I've tested with a power user scenario
- Not applicable to the orderbook layer specifically; the chart is sized
for a single market view.
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- Not added in this PR. The WebSocketManager already logs orderbook
lifecycle events via `DevLogger`. If Sentry traces are desired on
`subscribeToOrderbook` REST + WS handshake timing, can add in a
follow-up.

## **Pre-merge reviewer checklist**

- [x] 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.

---

## Appendix — quality gate

| Check | Result |
|---|---|
| `yarn lint:tsc` | clean (0 errors) |
| `yarn jest` (focused Predict suites: chart, details, hooks,
WebSocketManager) | 197 / 197 tests pass |
| ESLint on changed files | 0 errors. 7 pre-existing warnings
(deprecated controller types, `runAfterInteractions`, an existing
`React` shadow in mocks) all predate this branch — not introduced here.
|

## Appendix — files touched (high level)

- New: `app/components/UI/Predict/hooks/usePredictOrderbook.ts` (+ test)
- Modified: `WebSocketManager.ts` (orderbook subscription map, throttle,
lifecycle), `PolymarketProvider.ts` (passthrough + REST seed),
`PredictController.ts` (passthrough + `MESSENGER_EXPOSED_METHODS`),
`types/index.ts` (`OrderbookLevel`/`Snapshot`/`Callback`),
`providers/types.ts` (optional method in interface).
- Chart UX: `PredictCryptoUpDownChart.tsx` (+ test),
`PredictCryptoUpDownDetails.tsx`, `useCryptoUpDownChartData.ts` (+
test).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a new real-time orderbook subscription pipeline
(controller/provider/WebSocketManager) and feeds it into a
WebView-backed chart, which can impact live data correctness and
reconnect/unsubscribe behavior.
> 
> **Overview**
> **Adds live orderbook depth streaming for Predict crypto Up/Down
charts.** Introduces a reusable `usePredictOrderbook` hook, exposes
`PredictController.subscribeToOrderbook`, and implements
`PolymarketProvider.subscribeToOrderbook` with a REST `getOrderBook`
bootstrap that seeds the WS cache.
> 
> **Extends `WebSocketManager` to support `book` events.** Adds
per-token orderbook subscriptions with cached snapshot replay, REST
seeding, and a 250ms emit throttle; updates
unsubscribe/reconnect/cleanup logic and connection status to account for
orderbook subscribers.
> 
> **Updates chart wiring and formatting.** `PredictCryptoUpDownChart`
now forwards `orderbook` into `LivelineChart`, adds a compact
`CRYPTO_UP_DOWN_FORMAT_TIME` formatter, and expands test coverage across
the new hook, controller/provider passthroughs, WS behavior, and
formatter regression cases; also tweaks Crypto Up/Down details chart
height calculation constant usage and fixes websocket mock tests to use
OS-assigned ports.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
33c3365. 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: ghgoodreau <hunter.goodreau@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

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

Some of our workflows such as `build.yml` relies on the `main` workflow
file being run. This is to allow other branches to build while still
locking down the workflow from the main branch. This adds a source
branch input to the `build.yml` workflow to allow building from source
branches

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

With source input
<img width="267" height="427" alt="image"
src="https://github.com/user-attachments/assets/905d085d-f4dd-4374-bd28-cdf030e80e48"
/>


## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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 how `build.yml` resolves the git ref
for checkout and build metadata, which can break callers if they don’t
pass `source_branch` or if the value differs from the triggering ref.
> 
> **Overview**
> Makes `source_branch` a **required** input for
`.github/workflows/build.yml` and uses it consistently for all
checkouts/metadata (removing fallbacks to `github.ref_name`).
> 
> Adds `source_branch` to manual `workflow_dispatch` runs (defaulting to
`main`) and updates `expo-dev-build.yml` to pass the current ref name
into the reusable build workflow.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8bd6417. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **Description**

**note to reviewer:** please view the diff with the link to reduce
noise:
https://github.com/MetaMask/metamask-mobile/pull/30220/changes?w=1

Migrates the Stake screen and modal navigators from
@react-navigation/stack to
@react-navigation/native-stack([POC](https://docs.google.com/document/d/1_oDELkGRLUgaAeMs2NSzG9w1UyjsD5VSKQlW7yW5oZU/edit?tab=t.0#heading=h.gba8s4wq9tzu)),
and replaces getStakingNavbar + useEffect(setOptions(...)) with an
in-screen HeaderStandard on three Stake flows.

**Why**: Setting headers via useEffect + setOptions on native stack
causes a visible flicker on first paint (the default native header
renders, then is replaced); for these screens we now configure
headerShown: false at the route level and render the header in-screen
with HeaderStandard, which eliminates the flicker.

What changed

1. **Stake/routes/index.tsx**:
- createStackNavigator → createNativeStackNavigator for both
StakeScreenStack and StakeModalStack.
- Modal stack switched to clearNativeStackNavigatorOptions +
transparentModalScreenOptions (native-stack equivalents of the prior
preset).
- screenOptions.headerShadowVisible: false.
- headerShown: false on STAKE, STAKE_CONFIRMATION, UNSTAKE_CONFIRMATION,
EARNINGS_HISTORY.

2. StakeConfirmationView, UnstakeConfirmationView,
StakeEarningsHistoryView: replaced legacy header setup with
HeaderStandard; back button preserves the existing analytics events
(STAKE_CONFIRMATION_BACK_CLICKED, UNSTAKE_CONFIRMATION_BACK_CLICKED) via
useAnalytics.
**Note** I am leaving migration EarnWithDrawInputView.tsx out on purpose
because this navbar has more logic so it's better for the team to do the
migration

4. UnstakeConfirmationView.styles.ts: mainContainer height: '100%' →
flex: 1 so the footer (Cancel / Continue) lays out correctly under the
new flex parent + HeaderStandard.

5. EarnInputView: removed the now-redundant useEffect(() =>
navigation.setOptions({ headerShown: false })) (the route owns it).

6. Tests: navigation mocks now include goBack;
StakeEarningsHistoryView.test.tsx was rewritten to assert on the
rendered header title instead of setOptions / getStakingNavbar (which
the screen no longer calls).

Android test build:
https://github.com/MetaMask/metamask-mobile/actions/runs/25891826048


## **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. -->
**Please note that some of these flows are not representing what app is
behaving right now. I hardcoded some of the screens to see the
navigation behaviours**

### **Before**
<img width="382" height="786" alt="Stake before 1"
src="https://github.com/user-attachments/assets/9c6920e6-fa0e-4e39-aafe-2004dc7cfd0a"
/>
<img width="382" height="786" alt="stake after 2"
src="https://github.com/user-attachments/assets/69140cba-c85d-4989-909c-3e24b70080e8"
/>
<img width="382" height="786" alt="stake confirmation before"
src="https://github.com/user-attachments/assets/06e2a687-f797-4213-930e-269c343c004f"
/>

### **After**
<img width="382" height="786" alt="stake after 1"
src="https://github.com/user-attachments/assets/f0be027d-495c-49c1-8a27-fe76783fa2b8"
/>
<img width="382" height="786" alt="stake after 2"
src="https://github.com/user-attachments/assets/518f33a1-70d9-4e9f-b170-6d4213382698"
/>
<img width="382" height="786" alt="stake confirmation after"
src="https://github.com/user-attachments/assets/dc79e675-7838-44b3-9ece-1c28b39f9af4"
/>



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

## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [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 Stake navigation and header rendering, which can impact
screen transitions/back behavior and modal presentation; mitigated by
updated tests covering back actions and header titles.
> 
> **Overview**
> **Stake navigation is migrated to native-stack.** `Stake/routes`
switches from `@react-navigation/stack` to
`@react-navigation/native-stack`, applies native-stack modal presets
(`clearNativeStackNavigatorOptions` + `transparentModalScreenOptions`),
and sets `headerShown: false` on Stake-related screens to avoid native
header flicker.
> 
> **Stake headers are now rendered in-screen.** `StakeConfirmationView`,
`UnstakeConfirmationView`, and `StakeEarningsHistoryView` replace
`setOptions(getStakingNavbar(...))` with `HeaderStandard`, including
explicit back handlers that preserve existing MetaMetrics back-click
events; `UnstakeConfirmationView` layout is adjusted (`height: '100%'` →
`flex: 1`) to fit the new wrapper.
> 
> **Tests are updated accordingly.** View tests now assert rendered
header titles and verify `goBack` + analytics tracking via
`HeaderStandard` back button test IDs, and `EarnInputView` removes a
redundant `navigation.setOptions({ headerShown: false })` now handled at
the route level.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0c8c21d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…st, and IntroModal to design-system-react-native (#30183)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **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 updates `MultichainAccountConnect`, `PrivateKeyList`,
`AddressList`, and `IntroModal`/`LearnMoreBottomSheet` to use the
design-system `Toast`, `Text`, `BottomSheet`, and related types/imports.

## **Changelog**

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

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

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

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/MUL-1692

## **Manual testing steps**

```gherkin
Feature: Multichain account design system migration

  Scenario: user connects multichain accounts to a dapp
    Given the app receives a multichain account connection request
    When the user reviews the account and network permissions
    And the user confirms the connection
    Then the connection flow completes successfully
    And the permissions updated toast is shown

  Scenario: user copies an address from the Address List
    Given the user is viewing the Multichain Account Address List
    When the user taps the copy button for an address
    Then the address is copied to the clipboard
    And the copied-to-clipboard toast is shown

  Scenario: user reveals and copies a private key
    Given the user is viewing the Private Key List
    When the user enters the correct password
    And taps copy for a private key row
    Then the private key is copied with expiration
    And the copied toast is shown

  Scenario: user opens the Learn More bottom sheet
    Given the Multichain Accounts intro modal is visible
    When the user opens the Learn More bottom sheet
    Then the bottom sheet renders correctly
    And the checkbox enables the confirmation button
    And the back and close buttons dismiss the sheet
```

## **Screenshots/Recordings**

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


https://github.com/user-attachments/assets/6ad8efb8-d265-4287-becd-d19c6422e1fd

<img width="300" alt="Screenshot 2026-05-19 at 10 33 13 PM"
src="https://github.com/user-attachments/assets/86fb530f-9510-4489-bb8a-e16b16a830d4"
/> <img width="300" alt="Screenshot 2026-05-19 at 10 33 26 PM"
src="https://github.com/user-attachments/assets/2076920a-8594-4012-ad83-75f089f70a47"
/>

## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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 UI/refactor work, but it touches the multichain dapp connect
flow and private key copying screens where regressions could impact key
user journeys (toasts, navigation headers, and permission-updated
feedback). No changes to permission decisions or key material handling
beyond how feedback is displayed.
> 
> **Overview**
> Migrates Multichain Accounts UI surfaces (`MultichainAccountConnect`,
`AddressList`, `PrivateKeyList`, and `IntroModal/LearnMoreBottomSheet`)
off the legacy `ToastContext`/component-library primitives onto
`@metamask/design-system-react-native` (`toast`, `Toaster`,
`BottomSheet`, and `Text`). Copy and connect confirmation feedback now
uses design-system `toast()` calls with per-screen `<Toaster />`
mounting, including a favicon accessory for the *permissions updated*
toast in `MultichainAccountConnect`.
> 
> Updates `MultichainAddressRow` copy API to make `toastMessage`
optional and only trigger the legacy row-managed toast when **both**
`toastRef` and `toastMessage` are provided, enabling callers to own
toast behavior. Refreshes tests/stories accordingly (new toast mocks and
assertions, added header back-button test), and includes small UI tweaks
like tab bar padding and a reduced network-name max width in
`MultichainPermissionsSummary`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
903fa37. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Predict market cards on the Explore feed now open the Buy/Sell preview
as an in-place bottom sheet (matching the dedicated Predict feed) when
the `predictBottomSheet` feature flag is on, instead of routing to the
full-page bet slip.

### What changed

1. **Mounted `PredictPreviewSheetProvider` at the `HomeTabs` level** in
`app/components/Nav/Main/MainNavigator.js`, wrapping the
`Tab.Navigator`. The provider was previously only mounted inside
`PredictScreenStack`, so triggering `openBuySheet`/`openSellSheet` from
any other tab fell back to navigation. Mounting at `HomeTabs` makes the
sheet usable from Explore (and any future tab that needs it) while
keeping the existing in-Predict behavior untouched (`PredictScreenStack`
still mounts its own provider; the inner one shadows the outer for
usage).

2. **Why mount at `HomeTabs` and not inside the tab itself?**
`BottomSheet` from `@metamask/design-system-react-native` uses `absolute
inset-0` for its container. If the provider is mounted inside an
individual tab's content area, the sheet's parent is smaller than the
viewport and the sheet gets clipped at the top of the screen and below
by the bottom tab bar. Mounting at `HomeTabs` puts the sheet's parent at
the full-viewport `Home` Stack.Screen card, which is the smallest level
above the tab bar that gives correct dimensions.

3. **Fixed a duplicate-toast and stale-suppression bug** that the new
placement exposes. With both `HomeTabs` and `PredictScreenStack`
providers now mounted simultaneously while the user is inside the
Predict stack, both used to:
- independently fire the state-based "Try again" failure toast on
`activeOrder.error` transitions (no dedup in `ToastService`);
- increment the same `_providerSheetModeCount` counter that gates
`shouldSuppressLegacyOrderFailureToast()`, which then swallowed the
legacy failure toast in tabs/flows where the active provider could not
actually fire its own toast (e.g. Wallet/Trade/Money/Rewards, or
HomepageDiscoveryTabs which mounts in `disableBottomSheet` mode).

Replaced the module-level counter with a registration **stack**
(`_sheetModeProviders`). Each entry holds the provider's id and a
`hasBuyParams()` getter. The topmost (most recently mounted, innermost
in the tree) entry is the only "active" one:
- The state-based toast effect in `PredictPreviewSheetContext.tsx` bails
out unless the current provider is active — so only the innermost
provider fires the Retry toast.
- `shouldSuppressLegacyOrderFailureToast()` now consults the active
entry's `hasBuyParams()`, so the legacy toast is only suppressed when
the active provider will actually surface its own toast.

4. **Test coverage** for the multi-provider scenario in
`PredictPreviewSheetContext.test.tsx`:
- Topmost provider fires the failure toast exactly once when both are
mounted.
   - Outer provider becomes active again after the inner unmounts.
- Outer (sheet-mode) provider still fires when the inner provider is
mounted with `disableBottomSheet`.
- `shouldSuppressLegacyOrderFailureToast` correctly tracks the topmost
provider across mount/unmount.

5. Added `PredictPreviewSheetProvider` to the Predict barrel
(`app/components/UI/Predict/index.ts`) for consistency, and a rationale
comment in `MainNavigator.js` explaining why the wrap lives at
`HomeTabs` (so a future maintainer doesn't move it back inside a tab).

### Files touched

- `app/components/Nav/Main/MainNavigator.js`
- `app/components/Nav/Main/MainNavigator.test.tsx` (mock update)
- `app/components/UI/Predict/contexts/PredictPreviewSheetContext.tsx`
-
`app/components/UI/Predict/contexts/PredictPreviewSheetContext.test.tsx`
- `app/components/UI/Predict/index.ts`

## **Changelog**

CHANGELOG entry: Added in-place buy/sell bottom sheet to Predict market
cards on the Explore feed when the `predictBottomSheet` feature flag is
enabled.

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Predict bottom sheet on Explore feed

  Scenario: open buy sheet from Explore with feature flag ON
    Given the predictBottomSheet feature flag is enabled
    And the user is on the Explore tab
    And the Explore feed is showing Predict market cards

    When the user taps "Yes" on a Predict market card
    Then a buy preview bottom sheet opens in place
    And the sheet is anchored above the bottom tab bar (no clipping at the top or bottom)
    And the user can swipe the sheet down to dismiss it

  Scenario: navigation fallback when feature flag is OFF
    Given the predictBottomSheet feature flag is disabled
    And the user is on the Explore tab

    When the user taps "Yes" on a Predict market card
    Then the app navigates to the full-page bet slip (legacy behavior, unchanged)

  Scenario: Predict tab behavior is unchanged
    Given the predictBottomSheet feature flag is enabled
    And the user is on the Predict tab

    When the user taps an outcome on a market card
    Then the buy preview bottom sheet opens in place (existing behavior)
    And there is exactly one Retry toast if the order subsequently fails

  Scenario: only the topmost provider fires the failure toast
    Given the predictBottomSheet feature flag is enabled
    And the user opened and dismissed a buy sheet from Explore
    And the user navigated to the Predict tab and opened/dismissed another buy sheet
    And the user is now on the Predict tab with both sheets dismissed

    When the active Predict order transitions to a failed state
    Then the user sees exactly one "Try again" toast (not two)
    And tapping Retry reopens the most recently used sheet's market context
```

## **Screenshots/Recordings**

### **Before**

<!-- Recording: tapping Yes on an Explore market card navigates to the
full-page bet slip (or, with the earlier broken fix, opens a clipped
sheet). -->

### **After**

<!-- Recording: tapping Yes on an Explore market card opens the bottom
sheet in place, anchored above the tab bar, with no clipping. -->


https://github.com/user-attachments/assets/6bcb24e9-b81c-4c8f-b193-295440cd5805


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

#### Performance checks (if applicable)

- [ ] I've tested on Android
- [x] I've tested with a power user scenario
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics

> _Performance checks N/A: this PR only repositions an existing React
provider higher in the tree and refactors a module-level counter into a
registration stack. No new subscriptions/renders on hot paths; the
failure-toast effect now does strictly less work in non-active
providers._

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches top-level navigation composition and refactors module-scoped
toast suppression/dedup logic for Predict order failures; regressions
could affect bottom sheet rendering or toast behavior across tabs.
> 
> **Overview**
> Enables Predict market cards opened outside the Predict tab (e.g.
Explore) to use the in-place Buy/Sell preview bottom sheet by mounting
`PredictPreviewSheetProvider` above the home `Tab.Navigator`.
> 
> Refactors `PredictPreviewSheetContext` to handle multiple
simultaneously-mounted providers via a registration stack: only the
topmost sheet-mode provider can fire the state-driven Retry toast, and
legacy order-failure toast suppression now depends on the active
provider having remembered buy params (reducing stale suppression).
Tests are updated/added to cover multi-provider dedup and the new
suppression behavior, and `PredictPreviewSheetProvider` is exported from
the Predict barrel.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
51b7817. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

## **Description**

This PR prevents stale or effectively settled Predict markets from
occupying discovery feed surfaces.

- Adds an isolated `marketStaleness` utility for probability thresholds,
formal market status, daily/game expiry, time penalties, ranking
penalties, and highlighted-market exceptions.
- Applies the shared filter at hook/view-model boundaries for Predict
feed data, search results, featured carousel data, and the World Cup
feed without changing fetch parameters, page size, cursors, or backfill
behavior.
- Preserves highlighted-market metadata from
`PredictController.getMarkets`, keeping open highlighted markets pinned
first while still excluding closed/resolved highlighted markets.
- Removes the old `PredictMarketMultiple` exact `0`/`1` outcome filter
so display components render the outcomes already prepared by the shared
feed model.

Branch review summary:

- Base: `main`
- Branch:
`predict/PRED-747-filter-dead-resolved-markets-from-predict-feed-4-layer-staleness-system`
- Changed files: 16
- Commits: 7 scoped commits covering plan, utility, integration, tests,
and component cleanup

Automated coverage added/updated:

- Pure staleness policy tests for outcome thresholds, invalid
probabilities, outcome groups, formal status, time expiry, ranking
penalties, and highlighted-market behavior.
- Hook integration tests for feed, search, carousel, and World Cup
filtering.
- Controller tests for highlighted-market metadata and closed/resolved
highlight exclusion.
- Component regression coverage confirming `PredictMarketMultiple` no
longer owns price-based stale filtering.

## **Changelog**

CHANGELOG entry: Fixed stale Predict markets appearing in discovery
feeds and search results.

## **Related issues**

Fixes: PRED-747

Related: PRED-707, PRED-533, PRED-744

## **Manual testing steps**

```gherkin
Feature: Predict discovery staleness filtering

  Scenario: user views Predict discovery surfaces with stale and live markets
    Given Predict discovery data includes live markets, markets where all outcomes are at or beyond the dead probability thresholds, partially stale markets, and highlighted open markets

    When user opens the Trending tab, Predict search, featured carousel, or World Cup feed
    Then markets with no displayable live outcomes are not shown
    And partially stale markets show only live outcomes
    And open highlighted markets remain pinned first
    And closed or resolved highlighted markets are not shown
    And pagination state, cursors, and page sizes remain unchanged
```

## **Screenshots/Recordings**

N/A - this is feed data filtering/ranking behavior covered by automated
tests, with no intended layout or visual styling change.

### **Before** Left / **After** Right



https://github.com/user-attachments/assets/04690e72-6692-4601-9730-150eb6495b6b



https://github.com/user-attachments/assets/df5a33c5-43bf-49f5-bc42-af5603c0cca4



## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

- [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 - N/A, no new public API requiring JSDoc.
- [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. Label application should be
confirmed when creating the GitHub PR.

#### Performance checks (if applicable)

- [x] I've tested on Android - N/A, no native or platform-specific
rendering change; automated JS coverage was added for the affected data
paths.
  - Ideally on a mid-range device; emulator is acceptable
- [x] I've tested with a power user scenario - N/A, change is scoped to
Predict feed market filtering and does not touch wallet/account/token
scale paths.
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [x] I've instrumented key operations with Sentry traces for production
performance metrics - N/A, no new network operation or long-running
production operation was introduced.
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

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

## **Verification**

```bash
yarn jest app/components/UI/Predict/utils/marketStaleness.test.ts app/components/UI/Predict/hooks/usePredictMarketData.test.tsx app/components/UI/Predict/hooks/usePredictSearchMarketData.test.tsx app/components/UI/Predict/hooks/useFeaturedCarouselData.test.ts app/components/UI/Predict/hooks/usePredictWorldCup.test.ts app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.test.tsx --runInBand --coverage=false
yarn jest app/components/UI/Predict/controllers/PredictController.test.ts -t "getMarkets with market highlights" --runInBand --coverage=false
```

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes Predict discovery data shaping by filtering/reshaping outcomes
and re-ranking markets (including a new `isHighlighted` flag), which can
affect what users see and pagination behavior across multiple surfaces.
> 
> **Overview**
> Prevents *stale/settled* Predict markets from appearing in discovery
surfaces by introducing a shared `marketStaleness` policy
(`getVisiblePredictMarkets`) that drops closed/expired markets and
prunes “dead” outcomes (probability near 0/1), while re-ranking
remaining markets.
> 
> Applies this visibility filter in `usePredictMarketData`,
`useFeaturedCarouselData`, `usePredictWorldCupMarkets`, and the
empty-query fallback path in `usePredictSearchMarketData`, with tests
asserting pagination/cursors remain unchanged.
> 
> Updates `PredictController.getMarkets` to tag fetched highlight
markets with `isHighlighted` (kept pinned and exempt from staleness
filtering), and removes `PredictMarketMultiple`’s old price-based
outcome filtering so the UI renders outcomes as provided by the feed
model.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
443cd36. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…g) (#30413)

## **Description**

Mirrors the change from
[MetaMask/core#8633](MetaMask/core#8633) into
mobile ahead of the full migration to core, so the mobile copy of
`PerpsController` stays in sync with the upstream controller. The single
substantive change forwards `isInternal: true` in the `options` payload
when `PerpsController` submits a transaction via the
`TransactionController` messenger, matching the new core behavior that
flags controller-originated transactions as internal.

This keeps both codebases behaviorally identical until mobile retires
its local `PerpsController` in favor of the core package.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Perps internal transaction flag parity

  Scenario: user submits a perps transaction routed through PerpsController
    Given the Perps feature is enabled and the wallet is unlocked

    When user initiates a perps action that triggers PerpsController to submit a transaction
    Then the resulting TransactionController entry is marked as an internal transaction (isInternal: true)
    And the transaction completes with the same UX as before this change
```

## **Screenshots/Recordings**

### **Before**

N/A — behavioral parity change, no user-visible UI difference.

### **After**

N/A — behavioral parity change, no user-visible UI difference.

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

#### Performance checks (if applicable)

- [x] I've tested on Android
- [x] I've tested with a power user scenario
- [x] I've instrumented key operations with Sentry traces for production
performance metrics

## **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 options passed to `TransactionController:addTransaction`
for perps flows, which could affect how transactions are
classified/handled downstream (e.g., UI filtering or telemetry). Scope
is small and covered by updated unit tests.
> 
> **Overview**
> Perps transactions submitted via `PerpsController` now always forward
`isInternal: true` when calling `TransactionController:addTransaction`,
aligning mobile behavior with the upstream core controller.
> 
> Tests for `depositWithConfirmation` (including the `perpsDeposit` and
`perpsDepositAndOrder` paths) are updated to assert the new `isInternal`
flag in the messenger call options.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
93a877e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Updates the Predict World Cup main feed banner to match the latest Figma
card design. The banner now renders as a muted card with the World Cup
image, title, description, and a filled icon button in the footer.

Also updates the Predict World Cup feature flag image override from a
single URL to a structured `bannerImage` object containing `{ url,
width, height }`, so remote images can preserve their intended aspect
ratio while still falling back to a safe default aspect ratio when
dimensions are missing or invalid.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: PRED-895

## **Manual testing steps**

```gherkin
Feature: Predict World Cup main feed banner

  Scenario: user views the Predict feed with World Cup enabled
    Given the Predict World Cup feature flag is enabled
    And showMainFeedBanner and showWorldCupScreen are enabled

    When user opens the Predict feed
    Then the World Cup banner is displayed as a card with an image, title, description, and arrow button

  Scenario: user taps the World Cup banner
    Given the World Cup banner is visible on the Predict feed

    When user taps the banner
    Then user is navigated to the World Cup Predictions screen
```

## **Screenshots/Recordings**

### **Before**

N/A

### **After**

<img width="350" alt="Simulator Screenshot - mm-blue - 2026-05-20 at 15
16 24"
src="https://github.com/user-attachments/assets/088e1035-373e-4f23-bd3e-736d6930ff7b"
/>


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

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

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

## Testing

```bash
yarn jest app/components/UI/Predict/components/PredictWorldCupMainFeedBanner/PredictWorldCupMainFeedBanner.test.tsx app/components/UI/Predict/schemas/flags.test.ts app/components/UI/Predict/utils/resolvePredictFeatureFlags.test.ts --runInBand
```

Result: 3 test suites passed, 53 tests passed.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Moderate risk because it changes the remote feature-flag schema/type
for the World Cup banner image (from `bannerImageUrl` to structured
`bannerImage`) and updates banner sizing logic based on provided
dimensions, which could impact rendering if remote config is malformed.
> 
> **Overview**
> Updates the Predict World Cup main-feed banner to a card-style layout
with footer copy (title/description) and a filled arrow `ButtonIcon`,
while keeping the same navigation/tracking behavior.
> 
> Reworks the World Cup feature-flag image override from
`bannerImageUrl` to a structured `bannerImage` object (`{ url, width,
height }`) and introduces `getPredictWorldCupBannerImageAspectRatio` to
compute banner height from configured dimensions with a safe default
fallback; associated schema/type and unit tests are updated accordingly,
and the English banner description string is revised.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
580875f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators May 20, 2026
@pull pull Bot added the ⤵️ pull label May 20, 2026
@pull pull Bot merged commit 3fb3e97 into Reality2byte:main May 20, 2026
7 of 21 checks passed
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.