Skip to content

[pull] main from MetaMask:main#789

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

[pull] main from MetaMask:main#789
pull[bot] merged 31 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull

@pull pull Bot commented May 27, 2026

Copy link
Copy Markdown

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 9 commits May 27, 2026 06:59
<!--
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**

Simplify explore v2 implementation
<!--
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: Simplify explore v2 implementation


## **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]
> **Low Risk**
> UI refactor in Trending/Explore only—no auth or payments; minor risk
if removing CryptoMovers search row or Crypto predict visibility changes
analytics or UX subtly.
> 
> **Overview**
> Refactors Explore v2 (Trending) by **deduplicating UI and analytics**
across tabs and search.
> 
> Adds shared **`formatPercentChange`** and wires **perps** and **crypto
movers** pills to it instead of inline parsing/color logic. Introduces
**`PredictionsCarouselSection`** so **Now, Macro, RWAs, Crypto, and
Sports** reuse one horizontal carousel (header, skeleton,
`trackExploreInteracted`) instead of copy-pasted `renderPredictionItem`
blocks; **Macro/RWAs/Now/Sports** pass **`isEnabled`** from the predict
feature flag (Crypto leaves the default enabled).
> 
> **Search** drops the dedicated **`CryptoMoversFeedSearchRow`** export
and routes token/stock rows through **`TokenSearchRowItem`** only;
removes unused **`trackExploreEvent`**.
**`ExploreSectionResultsFullView`** stops passing an unused **`title`**
into list content.
> 
> Net effect: less duplicated Explore code with centralized
percent-change and predictions sections; verify Crypto-tab predict
gating and crypto-movers search row analytics if those paths still
matter.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1499bf8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#30617)

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

In Unified Buy v2, when a user entered an amount that fell outside one
provider's supported range (e.g. below MoonPay's minimum), the payment
method picker showed a blanket "No payment methods available" alert and
the provider picker silently dropped the affected provider. This was
misleading: other providers / payment methods were often still viable,
and the user had no way to learn _why_ a given option was unavailable.

This PR keeps every payment method and provider visible regardless of
quote state, and instead:

- **Greys out** rows that have no successful quote (sets `isDisabled` on
`ListItemSelect`, suppresses `onPress`).
- **Shows the provider's error message** (e.g. "Amount below minimum 25
USD") as the row subtitle, scoped to the row's provider — never bleeding
errors across providers.
- Falls back to a generic `Quote unavailable.` subtitle when the API
does not return an error string.
- Suppresses the "Previously used" / "Best rate" / "Most reliable" tag
on unavailable provider rows so the failure reason is the dominant
signal.
- Drops the previous list-level `visiblePaymentMethods` filter, since
the disabled-row UX makes silent filtering unnecessary.

## **Changelog**

CHANGELOG entry: Fixed a misleading "No payment methods available" error
in Buy when entering an amount outside a provider's limits. Unavailable
payment methods and providers are now shown greyed out with the specific
reason (e.g. "Amount below minimum 25 USD") instead of being hidden.

## **Related issues**

Fixes:
[TRAM-3556](https://consensyssoftware.atlassian.net/browse/TRAM-3556)

## **Manual testing steps**

```gherkin
Feature: Buy payment-method and provider selection error states

  Scenario: User enters an amount below the selected provider's minimum
    Given the user is on the Buy amount entry screen
    And the selected provider is MoonPay
    When the user enters an amount below MoonPay's minimum (e.g. 1 USD)
    And the user opens the "Pay with" payment selection modal
    Then every payment method is still listed
    And each payment method row is greyed out
    And each row's subtitle shows the provider's error message
      (e.g. "Amount below minimum 25 USD") or "Quote unavailable." as a fallback
    And tapping a greyed-out row does nothing

  Scenario: User enters an amount supported by only some providers
    Given the user is on the Buy amount entry screen
    And the entered amount is supported by Transak but not by MoonPay
    When the user opens the provider selection modal
    Then Transak is listed with its quote and tag (e.g. "Best rate")
    And MoonPay is listed but greyed out
    And MoonPay's row subtitle shows its error message
    And MoonPay's tag (e.g. "Previously used") is hidden
    And tapping MoonPay does not advance the flow

  Scenario: User enters a valid amount
    Given the user is on the Buy amount entry screen
    And the entered amount is supported by every provider/payment method
    When the user opens either selection modal
    Then all rows are enabled
    And each row shows its expected quote and tags
    And tapping a row selects it and closes the modal as before
```

## **Screenshots/Recordings**

### **Before**


https://github.com/user-attachments/assets/9b3294a6-a3df-4503-a8af-599d6b758eeb




<!-- Payment selection modal showing "No payment methods are available"
banner for an amount below MoonPay's minimum, despite other providers
being viable. -->

### **After**



https://github.com/user-attachments/assets/d6da5ee3-1648-45df-b968-6f1395318b98



<!-- Payment selection modal listing all payment methods greyed out with
the provider error message ("Amount below minimum 25 USD") as subtitle;
provider selection modal listing MoonPay greyed out with its error
subtitle while Transak remains selectable. -->

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

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


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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only changes in fiat on-ramp selection modals with expanded unit
tests; no auth, payments execution, or backend contract changes.
> 
> **Overview**
> Fixes misleading **Buy** UX when quotes fail for some options: payment
methods and providers stay visible instead of being hidden or replaced
by a blanket “no payment methods” state.
> 
> **Payment selection** stops filtering the list to methods with
successful quotes. Rows without a success quote (after quotes finish
loading) are **disabled**, show the **selected provider’s** API error or
`fiat_on_ramp.quote_unavailable`, and ignore taps.
`PaymentMethodListItem` accepts `quoteErrorMessage`, swaps subtitle for
that message when present, and uses `ListItemSelect` `isDisabled` / no
`onPress` on quote error.
> 
> **Provider selection** treats providers with an error and no matched
(non–custom-action) quote as **unavailable**: disabled row,
provider-specific error as subtitle (tags like “Previously used”
hidden), selection blocked. Providers with a valid matched quote stay
selectable even if a stale error exists for that provider.
> 
> Tests cover disabled rows, error scoping, loading behavior, and
custom-action quote edge cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
30025b4. 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: Darius Costolas <10818970+meltingice1337@users.noreply.github.com>
## **Description**

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

This PR tightens Money balance behavior after deposits/withdrawals and
on manual refresh so users see accurate state faster and clearer
feedback when data is stale or unavailable. It adds tx-confirmed balance
invalidation with bounded retries, hooks Money refresh into Wallet
pull-to-refresh (when feature-enabled), and updates Money balance UI
states to handle loading/retrying/error/feature-disabled/no-account
without misleading fallback values.

## **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: Improved Money balance refresh reliability after
deposits/withdrawals and added clearer unavailable/retry states on Money
screens.

## **Related issues**

Fixes:
- https://consensyssoftware.atlassian.net/browse/MUSD-811
- https://consensyssoftware.atlassian.net/browse/MUSD-831
- https://consensyssoftware.atlassian.net/browse/MUSD-836
- https://consensyssoftware.atlassian.net/browse/MUSD-841

## **Manual testing steps**

```gherkin
Feature: Money balance refresh and unavailable-state handling

  Scenario: user sees balance refresh after Money deposit or withdrawal confirms
    Given user has a Money account and submits a deposit or withdrawal
    When the transaction reaches confirmed
    Then Money balance data is re-fetched and updates without waiting for the next background poll

  Scenario: user pull-to-refreshes wallet home with Money enabled
    Given user is on Wallet home with Money account feature enabled
    When user pulls to refresh
    Then wallet balances and Money account balance both refresh in the same interaction

  Scenario: user sees explicit unavailable state and retry action when Money balance fetch fails
    Given user is on Money home and the balance fetch fails
    When the summary renders
    Then user sees "Balance unavailable" and can tap retry to re-fetch balance

  Scenario: user does not see misleading Money balance when account/feature is unavailable
    Given Money account feature is disabled or no primary Money account exists
    When Money balance components render
    Then balance/earnings content does not show fake numeric values and a clear unavailable state is shown
```

## **Screenshots/Recordings**

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

### **Before**

N/A

### **After**

Adding soon

## **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**
> Touches balance aggregation, React Query invalidation on tx confirm,
and user-visible Money/wallet-home balance UI; broad test coverage
limits regression risk but behavior changes affect core Money flows.
> 
> **Overview**
> This PR improves **Money balance accuracy and UX** after
deposits/withdrawals and when data is missing or stale.
> 
> **Refresh behavior:** `MoneyHomeView` adds pull-to-refresh that calls
`refetchBalance`, with error logging on failure.
`useMoneyAccountBalance` now exposes `isBalanceFetchError`,
`isBalanceFetching`, and `refetchBalance`, and clears formatted totals
on fetch errors. On confirmed Money deposit/withdraw transactions,
`useRefreshMoneyBalanceOnTxConfirm` invalidates balance queries with
exponential backoff until cached values change (wired via
`MoneyTransactionMonitor`). Shared `moneyTransactionGuards` dedupe tx
classification from toast logic.
> 
> **Display model:** A new `MoneyBalanceDisplayState` union drives
`MoneyBalanceSummary` and `MoneyBalanceCard` (feature disabled, no
account, loading, retrying, error with retry, unavailable, balance).
`useMoneyAccountInfo` centralizes feature flag + primary account.
**MoneyEarnings** only renders when `displayState.kind === 'balance'`,
avoiding “Balance unavailable” next to projected `$0.00` earnings.
`moneyFormatFiat` collapses sub-cent amounts to `$0.00`.
> 
> **Copy:** New strings for balance unavailable, retry, feature
disabled, and no account.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6c18e39. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Polish the buy/sell trade markers overlaid on the trader price chart in
the social leaderboard's `TraderPositionView`. The previous dots were
small, used non-token colors (`lightgreen`, `black` stroke), and were
hard to read on top of the chart line.

This PR:

- Resizes the inner colored disk to 16x16.
- Uses `theme.colors.success.default` for buys and
`theme.colors.error.default` for sells, matching the `+$...` / `-$...`
amount colors in the trades list below the chart.
- Adds a 4px ring colored `theme.colors.background.default` so the
marker visually punches through the chart line. The SVG `r` is set to
`innerRadius + strokeWidth / 2` so the ring sits fully outside the inner
disk.
- Adapts automatically to light/dark since both fill and stroke come
from the theme.

Only `TraderPriceChart.tsx` is touched; no API or behavior change beyond
the visual update.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Trader price chart trade markers

  Scenario: trader with recorded buys and sells
    Given I am viewing a trader position with both buy and sell trades in the visible time window
    When the price chart renders
    Then a 16px green dot is shown at each buy index, matching the green of the "+$..." amount in the trades list
    And a 16px red dot is shown at each sell index, matching the red of the "-$..." amount in the trades list
    And each dot is surrounded by a 4px ring matching the chart background

  Scenario: theme adaptation
    Given a trader position view with trade markers rendered
    When I switch between light and dark mode
    Then the ring around each dot continues to blend into the chart background
```

## **Screenshots/Recordings**

### **Before**

<img width="1207" height="788" alt="image"
src="https://github.com/user-attachments/assets/5a478fd8-ab80-462e-8e17-541b5aa6da05"
/>


### **After**

<img width="784" height="789" alt="image"
src="https://github.com/user-attachments/assets/6ff57ff5-057b-497f-91a2-2fcbbc64fe2e"
/>

## **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 (existing
`TraderPriceChart.test.tsx` still passes; markers are asserted by
testID, not color)
- [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.

#### Performance checks (if applicable)

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

N/A — purely visual change to SVG props on an existing component, no new
operations to trace.

## **Pre-merge reviewer checklist**

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Single-file SVG styling in TraderPriceChart with no API or data-flow
changes.
> 
> **Overview**
> Polishes **buy/sell trade markers** on the social leaderboard **trader
price chart** so they read clearly on top of the line.
> 
> Markers are **larger** (16px inner disk) with a **4px
background-colored ring**; SVG radius is derived so the ring sits
outside the fill. **Buy/sell colors** now use
`theme.colors.success.default` and `theme.colors.error.default` (aligned
with the trades list), with `theme.colors.background.default` for the
halo instead of `lightgreen` / `black`. The **chart line** always uses
`theme.colors.success.default`, dropping the separate light-mode green
path. **`priceDiff`** is no longer read in the component (import order
cleanup only).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
798663d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
… rule (#30642)

## **Description**

After #30590 excluded
`x86_64` from the production AAB (to silence Play's 16 KB page-size
warning), Play started rejecting the next release with:

> This release is not compliant with the Google Play 64-bit requirement.
> The following APKs or App Bundles are available to 64-bit devices, but
they only have 32-bit native code: 5174

Play's 64-bit rule requires every 32-bit ABI shipped to be paired with
its 64-bit equivalent:

| 32-bit ABI | Required 64-bit pair | Status before this PR |
|---|---|---|
| `armeabi-v7a` | `arm64-v8a` | paired |
| `x86` | `x86_64` | **unpaired** (x86_64 removed in #30590) |

Shipping `x86` without `x86_64` is what triggered the rejection.

### Fix

Drop `x86` too, making production ARM-only (`armeabi-v7a` +
`arm64-v8a`). This satisfies both:

- **64-bit rule** — `armeabi-v7a` is paired with `arm64-v8a`; no
unpaired 32-bit ABI remains.
- **16 KB rule** — no `x86_64` means no `x86_64 libconceal.so` /
`libsecp256k1.so` warnings.

`android/app/build.gradle`'s existing
`abiFilters(*reactNativeArchitectures())` plumbing automatically picks
up the new list, so this is a one-file change.

### User impact

Essentially zero. Real Android phones haven't shipped on x86 since ~2016
(the Asus Zenfone 2 era). Any modern Chromebook or emulator that could
run x86 is also `arm64-v8a` or `x86_64`-capable, and Play picks one of
those instead. `android/gradle.properties.github.dual-versions` (used
for BrowserStack real-device runs) has been on `armeabi-v7a,arm64-v8a`
all along, so this just brings the main release config in line with the
proven-real-devices target set.

### What's NOT changing

- `android/gradle.properties` (default, local dev) — still ships all
four ABIs for emulator support.
- `android/gradle.properties.github` (CI E2E) — still uses `x86_64`.
- `android/gradle.properties.github.dual-versions` — already
`armeabi-v7a,arm64-v8a`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: Google Play "This release is not compliant with the Google Play
64-bit requirement" rejection introduced by #30590.

Follow-up trackers for fixing the underlying 16 KB alignment so we can
re-add x86_64 / x86 if ever needed:
- #30591 — replace `react-native-fast-crypto` with
`react-native-quick-crypto` scrypt
- #30592 — drop the Facebook Conceal dependency from
`react-native-keychain`

## **Manual testing steps**

```gherkin
Feature: Production AAB no longer contains x86 or x86_64 ABIs

  Scenario: A production release build is generated
    Given a clean checkout of this branch
    When the production AAB is built via the standard release workflow
      (cp android/gradle.properties.release android/gradle.properties
       before ./gradlew bundleProdRelease)
    Then `unzip -l app-prod-release.aab | grep '/lib/'` shows only
      `lib/arm64-v8a/` and `lib/armeabi-v7a/` entries
    And no `lib/x86/` or `lib/x86_64/` entries appear

  Scenario: Local dev on x86_64 emulator still works
    Given default android/gradle.properties (unchanged) is in use
    When `yarn android` is run
    Then x86_64 native libs are still produced and the app installs/runs

  Scenario: CI E2E on x86_64 emulator still works
    Given android/gradle.properties.github (unchanged) overlays for CI
    When `./gradlew assembleProdDebug` runs
    Then the resulting APK contains `lib/x86_64/` and Detox tests pass

  Scenario: Play Console upload succeeds
    Given a production AAB built from this branch
    When the AAB is uploaded to the Play Console
    Then the 64-bit compliance check passes
    And no 16 KB page-size warning appears for x86_64 .so files
```

## **Screenshots/Recordings**

### **Before**

Play Console:
> Error: This release is not compliant with the Google Play 64-bit
requirement.
> The following APKs or App Bundles are available to 64-bit devices, but
they only have 32-bit native code: 5174

### **After**

64-bit compliance check passes; 16 KB warning for `x86_64` .so files
remains cleared.

## **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
- [ ] I've included tests if applicable — N/A (build-config change;
verification is via the `unzip -l` check on the produced AAB and the
Play Console upload)
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable — N/A
- [ ] I've applied the right labels on the PR — `team-mobile-platform`

#### Performance checks (if applicable)

- [x] I've tested on Android — `gradle.properties.github.dual-versions`
has been on `armeabi-v7a,arm64-v8a` for BrowserStack real-device runs
without issue.
- [ ] I've tested with a power user scenario — N/A (no JS behavior
change)
- [ ] I've instrumented key operations with Sentry traces — N/A

## **Pre-merge reviewer checklist**

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Single release Gradle property change; default and CI gradle files
still ship x86/x86_64 for dev and emulators.
> 
> **Overview**
> Production release builds now package **only** `armeabi-v7a` and
`arm64-v8a` native ABIs by updating `reactNativeArchitectures` in
`android/gradle.properties.release` (removing `x86`).
> 
> That pairs with the earlier removal of `x86_64` so Play’s **64-bit
rule** is satisfied—`x86` without `x86_64` was causing rejection—while
keeping the **16 KB page-size** workaround (no bundled `x86_64` `.so`
from keychain/crypto AARs). Comments in the same file document why both
Intel ABIs are omitted; `ndk.abiFilters` in `app/build.gradle` already
reads this property, so no Gradle code changes.
> 
> Local dev (`gradle.properties`) and CI E2E
(`gradle.properties.github`) are unchanged.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
626b4e5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until 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**
https://consensyssoftware.atlassian.net/browse/RWDS-1333
https://consensyssoftware.atlassian.net/browse/RWDS-1330
https://consensyssoftware.atlassian.net/browse/RWDS-1323

Updated VipTierRow background when expanded and collapsed
Updated equity allocation view
Updated localized text in conjunction with
consensys-vertical-apps/va-mmcx-rewards#586

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

## **Changelog**

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

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

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

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**

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

### **Before**

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

### **After**
<img width="1179" height="2556" alt="Simulator Screenshot - E2E Test -
2026-05-26 at 15 53 43"
src="https://github.com/user-attachments/assets/2268f9fd-433d-46a0-8265-34c2d3985d39"
/>
<img width="1179" height="2556" alt="Simulator Screenshot - E2E Test -
2026-05-26 at 15 54 02"
src="https://github.com/user-attachments/assets/fc114bfb-c362-4543-a70d-130cd7ce4f7e"
/>


https://github.com/user-attachments/assets/97582934-8935-4966-8944-9184031bfbd1



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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**
> Wide contract and UI changes across VIP dashboard, tiers, and points;
mismatched backend rollout could break copy or allocation display, but
scope is rewards UI only with heavy test updates.
> 
> **Overview**
> Aligns the mobile VIP rewards experience with an updated rewards API
contract and refreshed tier/allocation UI.
> 
> **API & types:** `VipPointsAllocationDto` becomes
`VipEquityAllocation` (`max` → `threshold`). `VipLocalizedTextDto` is
renamed/restructured (`periodTitle`, equity locked/unlocked copy,
`totalPointsTitle`; drops `progressToNextTier`, `statusMessage`, old
points-allocation keys). `equityRebateBps` is removed from `VipTierDto`.
Mocks/tests across controller, reducer, and UI are updated to match.
> 
> **VIP dashboard (`RewardsVipView`):** Drops the equity-rebate fee
carousel tile and tier-resolution IIFE. Tier progress subline is built
locally via `rewards.vip.progress_to_next_tier` and
`formatCompactValue(remainingPointsToNextTier)`. Volume uses
`periodTitle` only (no status line). Points section uses the new equity
locked/unlocked localized strings.
> 
> **Points section:** Replaces tier-based equity-rebate radial logic
with a threshold gate (`earned >= threshold`) and a single
`earned/threshold` ring label; progress stroke clamps at 100% when
percent > 100.
> 
> **Tier list (`VipTierRow`):** Adds an animated gold `LinearGradient`
overlay on the current tier (fades on collapse while staying mounted).
Layout/spacing tweaks; fee tile width 157 → 168.
> 
> **Volume:** Removes the “on track” status row and `status` prop.
> 
> **i18n:** Adds `rewards.vip.progress_to_next_tier`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
be8b0b2. 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: VGR <VanGulckRik@gmail.com>
…#28909)

## **Description**

On the confirmation screen for an mUSD conversion, the
`GasFeeTokenToast` was overlaying the screen with "You're paying this
network fee with mUSD." That notification duplicated information already
shown on the confirmation screen and clashed visually with it — and the
toast's premise is misleading here anyway, because mUSD conversions are
sponsored at the relay level (no network fee is paid by the user).

This PR suppresses the toast specifically for
`TransactionType.musdConversion`. The toast still fires for every other
transaction that uses a non-native gas fee token (e.g. paying gas in
USDC on a regular send/swap), where it continues to serve its original
reassurance purpose.

**Why a type check instead of a token-symbol check:** the suppression is
about "this is a relay-sponsored, fee-free flow," not "the selected
token is mUSD." Tying it to `TransactionType` keeps the scope correct
and extends cleanly if another relay-sponsored flow ships later.

## **Changelog**

CHANGELOG entry: Fixed a UI clash on the mUSD conversion confirmation
screen where a duplicate network-fee toast was shown.

## **Related issues**

Fixes: MUSD-1

## **Manual testing steps**

```gherkin
Feature: mUSD conversion confirmation does not show the gas-fee-token toast

  Scenario: user opens mUSD conversion confirmation
    Given the user has a balance in a supported stablecoin (e.g. USDC)
    And the user is on the Money homepage

    When the user taps "Convert" and proceeds to the confirmation screen
    Then no "You're paying this network fee with ..." toast is shown

  Scenario: regular transaction with non-native gas token still shows the toast
    Given the user has a transaction that pays gas with a non-native token (e.g. USDC, not as an mUSD conversion)

    When the confirmation screen loads and the non-native gas token is selected
    Then the "You're paying this network fee with USDC." toast is shown as before
```

## **Screenshots/Recordings**

### **Before**


### **After**


## **Pre-merge author checklist**

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

#### Performance checks (if applicable)

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Confirmation UI-only guard on an existing toast; no auth, payment, or
transaction submission logic changes.
> 
> **Overview**
> **`GasFeeTokenToast`** no longer shows the “paying network fee with …”
toast on **mUSD conversion** confirmations, where relay-sponsored flows
already surface fee context and the message was redundant or misleading.
> 
> The component skips its effect when transaction metadata matches
**`TransactionType.musdConversion`** via **`hasTransactionType`** and an
**`IGNORED_TRANSACTION_TYPES`** list (extensible for other sponsored
flows). Other transactions that use a non-native gas fee token still get
the toast. A unit test asserts **`showToast`** is not called for mUSD
conversion.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bfc670c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Enables money account keyring to be 7702 upgradeable. 

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

Already enabled in core: MetaMask/core#8687

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


## **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**
> Small allowlist and enum addition with a unit test; no auth, payment,
or broad transaction-path changes.
> 
> **Overview**
> Treats **Money Keyring** accounts as EIP-7702–capable in mobile,
matching core support for money-account smart-account upgrades.
> 
> Adds `ExtendedKeyringTypes.money` and includes it in
`KEYRING_TYPES_SUPPORTING_7702` so `accountSupports7702` returns true
for money accounts (same as HD and simple keyrings). A unit test locks
in that behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6124cb1. 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**

### Why

E2E build reuse could select artifacts from a prior workflow run using
only the shared `build-source-hash` status on the commit SHA. If
multiple runs existed for the same SHA/fingerprint, CI had limited proof
that the downloaded app/APKs came from the exact run selected for reuse.

For Detox, this matters because the tests must always run against the
correct native build. If reuse is wrong, failures can look like flaky
E2E tests while the real issue is stale or mismatched build artifacts.

### Before

- Reusable E2E builds were matched by fingerprint and artifact names.
- The fingerprint status was checked at the commit level, not tied to
the candidate workflow run.
- Artifact reuse did not validate build metadata before skipping the
native build.
- Candidate runs still in progress could be considered.

### After

- Reusable runs must be completed.
- The `build-source-hash` status must match the fingerprint and point to
the specific candidate workflow run.
- iOS and Android E2E build jobs now publish metadata artifacts.
- Reuse now downloads and validates metadata before downloading/reusing
native artifacts.
- Metadata validation checks:
  - schema version
  - fingerprint
  - platform
  - build type
  - MetaMask environment
  - workflow run id
  - head SHA
  - expected artifact names

If metadata is missing or does not match, CI falls back to a fresh
native build.

## Impact

This makes E2E build reuse stricter and safer. First runs after this
change will likely rebuild because older artifacts do not have metadata
yet. After metadata exists, reuse still works, but only when CI can
prove the artifacts match the current E2E build request.

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

## **Changelog**

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

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

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

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin

Scenario 1: CI builds fresh artifacts on first run
Given the PR has no reusable E2E build artifacts with valid metadata for the current fingerprint
When CI runs the Android and iOS E2E build jobs on the current runner
Then the jobs fall back to fresh native builds, upload app/APK artifacts, upload build metadata artifacts, and Detox runs against the freshly built artifacts

Scenario 2: CI safely reuses artifacts on second run
Given a previous completed current-run CI build produced E2E artifacts and matching build metadata for the same fingerprint
When CI is re-run without changing native build inputs
Then the build jobs find the prior run, validate the metadata, skip the native build, download the matching app/APK artifacts, and Detox runs against those artifacts

Scenario 3: CI falls back when metadata is invalid or missing
Given a reusable run is found but its E2E build metadata is missing or does not match the current build request
When CI evaluates the reusable build
Then the metadata validation fails, artifact reuse is skipped, and CI performs a fresh native build


```

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

- [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
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-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).
- [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**
> Changes only GitHub Actions reuse logic but directly affects which
native binaries Detox runs against; misconfiguration could force extra
builds or, worse, reuse wrong artifacts until metadata checks fail.
> 
> **Overview**
> Tightens **CI E2E native build reuse** so Detox only skips a full
build when artifacts are tied to a specific prior workflow run, not just
a matching fingerprint on the commit.
> 
> **`find-reusable-build`** now requires **completed** runs, matches
`build-source-hash` via per-status `target_url` pointing at that run
(replacing commit-level combined status), and can require a **metadata
artifact** alongside app/APK artifacts.
> 
> **Android and iOS E2E build workflows** publish a versioned **build
metadata** JSON artifact, download and **jq-validate** it (fingerprint,
platform, build type, environment, run id, head SHA, artifact names)
before reusing binaries, and fall back to a fresh native build on any
mismatch. Reuse decisions are surfaced in **job step summaries**.
> 
> **`ci.yml`** renames the fingerprint job to
**`native-build-fingerprint`** and wires E2E builds to its output;
**`post-build-source-hash`** gains optional **`post-status`** (skipped
on Namespace trial runs while still emitting the fingerprint).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
734d950. 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 27, 2026
@pull pull Bot added the ⤵️ pull label May 27, 2026
caieu and others added 19 commits May 27, 2026 12:19
## **Description**

Fixes a Predict market details issue where placing a bet from a large
multi-outcome market, such as the World Cup winner market with 64
outcomes, could stall before the transaction-added event appeared.

The details screen was keeping broad outcome price work active behind
the buy sheet. For 64 outcomes, that meant REST price polling and live
price subscriptions for every YES/NO token while the buy flow was also
initializing transaction setup. This change exposes buy sheet open state
from the preview sheet context and pauses the market details
open-outcome price refresh while the buy sheet is mounted.

Changes included:

- Add `isBuySheetOpen` to `PredictPreviewSheetContext`.
- Disable broad open-outcome REST polling and live subscriptions from
`PredictMarketDetails` while the buy sheet is open.
- Keep existing outcome data available so the selected buy flow still
has its market/outcome context.
- Add regression coverage for the buy sheet open state and the
64-outcome paused-refresh behavior.

## **Changelog**

CHANGELOG entry: Fixed a bug that could delay prediction bet placement
from large market details screens.

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Predict large market bet placement

  Scenario: user places a bet from a 64-outcome market details screen
    Given Predict is enabled
    And the user opens the World Cup winner market details screen
    And the market has many open outcomes

    When the user taps Buy on an outcome
    And the buy sheet opens
    Then the market details screen pauses broad open-outcome price polling and live subscriptions

    When the user proceeds with the bet
    Then transaction setup should continue without the details screen generating repeated RPC degraded logs from broad outcome refresh work
```

Automated testing run:

```bash
node .yarn/releases/yarn-4.14.1.cjs jest app/components/UI/Predict/contexts/PredictPreviewSheetContext.test.tsx --runInBand
node .yarn/releases/yarn-4.14.1.cjs jest app/components/UI/Predict/views/PredictMarketDetails/PredictMarketDetails.test.tsx --runInBand
git diff --check
```

## **Screenshots/Recordings**

### **Before**

Reporter-provided recording showed bet placement from the World Cup
winner market details screen getting stuck before transaction-added logs
appeared, followed by repeated RPC degraded logs.

### **After**

No after recording captured in this session. Behavior is covered by
targeted unit tests for pausing the broad details price refresh while
the buy sheet is open.

## **Pre-merge author checklist**

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

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


[PRED-930]:
https://consensyssoftware.atlassian.net/browse/PRED-930?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Targeted performance fix: toggles existing price hooks off during buy
sheet only; no auth, payment, or order logic changes.
> 
> **Overview**
> Fixes stalls when placing bets from **large multi-outcome** Predict
market details (e.g. 64 teams) by stopping background price work while
the buy sheet is open.
> 
> **`PredictPreviewSheetContext`** now exposes **`isBuySheetOpen`**
(true while buy preview params are mounted; **`false`** in the
navigation fallback outside the provider). **`PredictMarketDetails`**
uses it to gate **`useOpenOutcomes`**: broad **REST polling** (~2s) and
**live price subscriptions** for every open outcome token pause when the
buy sheet is visible and resume on dismiss. **`useOpenOutcomes`**
accepts an optional **`enabled`** flag and passes empty queries with
polling disabled when off, while still returning cached outcome
structure for the buy flow.
> 
> Tests cover buy-sheet open/close state and the 64-outcome pause/resume
behavior for **`usePredictPrices`** / **`useLiveMarketPrices`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f55989d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…n Home (MUSD-838) (#30596)

## **Description**

The Money Account Home Card was choosing its CTA variant ("Earn" / "Add"
/ "Get started") from the user's account state, not from whether another
primary CTA already exists on Home. As a result, an empty-balance
non-new user saw a Primary "Earn" button on Home at the same time the
onboarding stepper rendered its primary CTA (two primaries on one
screen), and a funded user saw a Secondary "Add" button even when no
other primary CTA existed.

This change decouples variant from account state. A single derived
boolean — `hasOtherPrimaryCtaOnHome`, aliasing the existing
`selectWalletHomeOnboardingFlowVisible` selector that already gates the
onboarding stepper — drives the variant for every branch. Labels are
untouched.

## **Changelog**

CHANGELOG entry: Fixed the Money Account Home Card showing two primary
CTAs on Home when the onboarding stepper is visible, and showing a
non-primary "Add" CTA when no other primary CTA exists.

## **Related issues**

Fixes:
[MUSD-838](https://consensyssoftware.atlassian.net/browse/MUSD-838)

## **Manual testing steps**

```gherkin
Feature: Money Account Home Card CTA variant

  Scenario: Money CTA is secondary when the onboarding stepper is visible
    Given a wallet whose onboarding stepper is visible on Home
    When the user lands on Home
    Then the Money Account card's button renders as a secondary CTA

  Scenario: Money CTA is primary when no other primary CTA exists on Home
    Given a wallet with the onboarding stepper dismissed or completed
    When the user lands on Home
    Then the Money Account card's button renders as the primary (black) CTA

  Scenario: Earn and Add follow the same conditional logic
    Given a funded Money Account on Home
    When the onboarding stepper is hidden
    Then the "Add" button renders as primary
    When the onboarding stepper is visible
    Then the "Add" button renders as secondary
```

## **Screenshots/Recordings**

### **Before**

### **After**

## **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
- [ ] I've tested with a power user scenario
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics

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


[MUSD-838]:
https://consensyssoftware.atlassian.net/browse/MUSD-838?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only button styling on the Money balance card with regression
tests; no auth, data, or navigation logic changes beyond variant
selection.
> 
> **Overview**
> **MoneyBalanceCard** now picks **Primary vs Secondary** for Earn / Add
/ Get started from whether another primary CTA is already on Home
(`hasOtherPrimaryCtaOnHome`, wired to
`selectWalletHomeOnboardingFlowVisible`), instead of hard-coding
variants by balance state alone.
> 
> **Empty** and **funded** branches were updated so the card is
**Secondary** when the wallet-home onboarding stepper is visible and
**Primary** when it is not—fixing duplicate primaries and a funded
**Add** that stayed secondary with no competing CTA. New-user
label/testID logic is unchanged; only variant selection is unified.
Tests assert variant behavior across empty, funded, and new-user cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
877baae. 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**

`Predict Market Details Opened` and `Predict Trade Transaction` had no
way to attribute conversions originating from the Explore page —
`EXPLORE` was simply missing from `ENTRY_POINT`, and the Explore→Predict
navigation never passed one.

- Adds `ENTRY_POINT.EXPLORE = 'explore'` to `PredictEventValues` and the
`PredictEntryPoint` union (mirrors `SOURCE.EXPLORE` in Perps)
- `PredictionCarouselRowItem` and `PredictionSearchRowItem` hardcode
`ENTRY_POINT.EXPLORE` — same pattern as `PerpsRowItem`
- `SportsTab` sets it on the one direct `PredictMarket` usage
- Extracts `useResolvedPredictEntryPoint` hook to centralise the context
> prop > trending session > default priority that was duplicated across
5 card components; fixes a pre-existing bug where the session manager
could override an explicit prop

<!--
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: add explore entrypoint attribution in predict

## **Related issues**

Fixes:
https://consensyssoftware.atlassian.net/browse/PRED-929?issueKey=PRED-929&subProduct=jira-software
& https://consensyssoftware.atlassian.net/browse/ASSETS-3271

## **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**
> Analytics and UI wiring only; no auth, payments, or data-handling
changes. Behavior change is clearer entry-point attribution and a small
priority bugfix.
> 
> **Overview**
> Adds **`ENTRY_POINT.EXPLORE`** (`explore`) to Predict analytics types
and passes it from **Explore** surfaces: prediction carousel/search rows
and **Sports** tab market cards—so market opens and trades can attribute
to Explore.
> 
> Introduces **`useResolvedPredictEntryPoint`** to replace duplicated
logic across Predict market cards. Resolution order is **context → prop
→ trending session → `predict_feed` default**. This also fixes a bug
where an active trending session could override an **explicit**
`entryPoint` prop.
> 
> Card components and related tests are updated to use the hook and to
assert that **explicit entry points win over trending**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1c32f7f. 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**

Adds the Pay With step to QuickBuy with a token list backed by
`sourceTokenOptions`. Also moves the exchange rate tag into the toolbar
to match Figma and adds empty-state copy.

<img height="800" alt="Simulator Screenshot - iPhone 17 Pro - 2026-05-26
at 18 04 46"
src="https://github.com/user-attachments/assets/dde60d07-f9a5-4cdc-9cd8-1c32a634466a"
/>

## **Changelog**

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

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

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

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

<!--
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 which source token funds a bridge buy and resets amount state
on switch; behavior is feature-flagged but affects real swap funding
paths when enabled.
> 
> **Overview**
> Adds a **Pay With** bottom-sheet step to Top Traders Quick Buy: the
footer pill navigates to `payWith`, lists `sourceTokenOptions` with
optional multi-chain chips (`PredictChipList`), and selecting a token
runs `handleSelectSourceToken` (clears amount/slider) before returning
to the amount screen. **`payWithSheet`** is turned on in
`TOP_TRADERS_QUICK_BUY_FEATURES`.
> 
> **UI polish:** the exchange rate moves from the amount section into
**`QuickBuyToolbar`** (with matching loading skeleton); empty-state
strings for no pay tokens / “All” filter are added.
> 
> **`PredictChipList` refactor:** scroll math lives in
`calculateChipScrollX` + `useChipScrollList`; chips can show optional
icons, custom Tailwind/test IDs, and an optional gesture-handler
horizontal scroll for reuse (e.g. chain filter).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
405bd26. 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?
-->

## **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/MMQA-1802

## **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**
> Changes are limited to test infrastructure and a new
performance/system spec; production app code is untouched.
> 
> **Overview**
> Adds a **Playwright/Appium system test** that logs in, sends a tiny
ETH amount via the redesigned send flow, confirms, opens Activity, and
waits for on-chain **Confirmed** status (5-minute timeout).
> 
> Extends the **dual-framework test layer** so the same page objects
work under Detox and Appium: `PlaywrightGestures.waitAndTap` now accepts
an optional **`timeout`** for display/enable waits;
send/confirmation/activity/tab-bar flows use **`encapsulatedAction`**
with platform-specific locators (e.g. Ethereum network chip + ETH row,
iOS recipient `textfield`, numpad keys by XPath on iOS);
**`walletSendButton`** is encapsulated and tapped via
**`UnifiedGestures`**; Activity tab and confirm footer get Appium paths;
**`ActivitiesView.waitForTransactionConfirmed`** polls status text up to
~120s on Appium.
> 
> New send helpers: **`enterAmountViaNumpad`**,
**`selectRecipientAccount`**, plus Detox/Appium splits for continue,
review, and recipient entry.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8efe346. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ettings Drawer (#30543)

## **Description**

Adds a shared `Pressable` primitive
(`app/component-library/components-temp/Pressable/`) that replaces
`TouchableOpacity`'s full-subtree opacity dim with a token-based
background swap on press. Callers pick a semantic variant (`section` /
`subsection` / `default` / `muted` / `transparent` / `none`) and the
component owns the resting → pressed token pair, so the broader
`TouchableOpacity` migration becomes consistent without per-call-site
token guessing. Ships RN core + `react-native-gesture-handler` variants
(RNGH scroll trees need their own `Pressable` to avoid Android gesture
conflicts) and defaults `accessibilityRole="button"` to preserve the
implicit role `TouchableOpacity` provided.

Migrates `SettingsDrawer` as the first consumer. This fixes the
pure-black-mode regression where the previous opacity dim made elevated
rows look invisible against the `#000000` backdrop — pressed rows now
shift to `background.defaultPressed` instead of dimming.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Settings row press feedback

  Scenario: user presses a Settings row in light mode
    Given the app is open in light mode
    When user navigates to Settings and presses any row
    Then the row background briefly shifts to the pressed token (no subtree dim)

  Scenario: user presses a Settings row in pure-black mode
    Given MM_PURE_BLACK_PREVIEW=true is set in .js.env and the app is rebuilt
    When user navigates to Settings and presses any row
    Then the row background briefly shifts to #222226 and remains clearly visible
    And the row does not appear to "disappear" against the pure-black backdrop
```

## **Screenshots/Recordings**


https://github.com/user-attachments/assets/15251791-a867-4f96-a9d4-7eaca4b1c626


https://github.com/user-attachments/assets/c72bc988-f1e8-4fcd-98d4-91b4a11b5a77

### **Before**

### Non-Pure Black


https://github.com/user-attachments/assets/838a11c1-ad14-4e17-8df3-e43ef906e3e8

### Pure Black


https://github.com/user-attachments/assets/83ae56b1-916a-47e0-b92f-545eab8ff111

### **After**

### Non-Pure Black


https://github.com/user-attachments/assets/a0975b7e-d62c-4c4c-b3e9-9b2a39876d1a

### Pure Black


https://github.com/user-attachments/assets/d0d2b706-6b2a-4ac1-8026-21e151adf4f2

## **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
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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]
> **Low Risk**
> Localized UI/interaction and a single settings row consumer; main
follow-up is overriding accessibilityRole where rows are not semantic
buttons.
> 
> **Overview**
> Introduces a shared **`Pressable`** primitive under
`components-temp/Pressable` (plus **`PressableGH`** for RNGH scroll
trees) to standardize press feedback: on press it layers
**`colors.background.pressed`** over the caller’s style instead of
**`TouchableOpacity`** subtree opacity. **`accessibilityRole`** defaults
to **`button`**; tests, README, Storybook story, and registry entry are
included.
> 
> **`SettingsDrawer`** is migrated as the first
consumer—**`TouchableOpacity`** → **`Pressable`**, and the row wrapper
no longer sets a resting **`backgroundColor`** on the outer styles
(pressed feedback comes from the new overlay).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4260311. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Adds a Primary CTA haptic impact when the user presses the **Trade**
button on a related-asset row in the What's Happening expanded card.

- The Trade button is a primary commit action that opens the Perps
market detail flow. Per the haptics catalog, `ImpactMoment.PrimaryCTA`
is the moment reserved for primary surface commits (Buy/Trade/Save) and
is the correct mapping here.
- The haptic fires only when there is a valid `hlPerpsMarket` symbol
(i.e., the same guard that gates navigation and analytics). If the press
is a no-op, no haptic plays.
- Implemented via the project's `util/haptics` facade (`playImpact` +
`ImpactMoment`), not by importing `expo-haptics` directly — consistent
with the existing eslint rule and the catalog-driven haptics
architecture used elsewhere (e.g. `PerpsSlider`).

## **Changelog**

CHANGELOG entry: Added haptic feedback to the Trade button in the What's
Happening detail card.

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Haptic feedback on What's Happening Trade CTA

  Scenario: User taps Trade on a related perps asset
    Given the user has opened a What's Happening card with at least one related perps asset
    And the device has system haptics enabled
    When the user taps the "Trade" button next to a related asset
    Then a medium "Primary CTA" haptic impact is felt
    And the app navigates to the Perps market details screen

  Scenario: Row without a perps market does not render Trade
    Given a related asset has no hlPerpsMarket symbol
    When the user views the row
    Then no Trade button is shown
    And no haptic fires
```

## **Screenshots/Recordings**

### **Before**

No haptic on Trade press.

### **After**

Primary CTA haptic fires on Trade press (see attached screenshot of the
affected screen in the PR description).

## **Pre-merge author checklist**

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
- [ ] I've tested with a power user scenario
- [ ] 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.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small UX addition behind existing trade guards; no auth, data, or
navigation logic changes beyond calling the shared haptics helper.
> 
> **Overview**
> Adds **Primary CTA** haptic feedback when users tap **Trade** on a
related perps asset row in the What's Happening detail card, using
`playImpact(ImpactMoment.PrimaryCTA)` from `util/haptics`.
> 
> The impact runs only inside the existing trade handler after a valid
`hlPerpsMarket` symbol is confirmed—the same guard as navigation and
analytics—so no-op rows never trigger haptics. **Unit tests** mock the
haptics facade and cover both successful Trade press and missing-market
cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9f94739. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
…nts (#30621)

## **Description**

The `TradingService` analytics events (`TradeTransaction`,
`PositionCloseTransaction`) already read `vipTier` and `vipDiscount`
from `trackingData`, but no caller was populating those fields. This PR
wires VIP program context through every trading flow:

- **`PerpsOrderView`** — adds `vipTier` / `vipDiscount` to the
`trackingData` passed to `placeOrder`, so `#trackOrderResult` includes
them.
- **`PerpsClosePositionView`** — adds the same fields to
`closePosition`'s `trackingData`, so `#buildCloseEventProperties`
includes them.
- **`PerpsFlipPositionConfirmSheet` / `usePerpsFlipPosition`** — extends
`handleFlipPosition` to accept `TrackingData` and passes VIP context
through to `TradingService.flipPosition`.

All three views obtain `vipTier` via the existing `useVipTier()` hook
and `vipDiscount` from `feeResults.feeDiscountPercentage`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: VIP tier and discount in trading analytics

  Scenario: VIP data included in place-order analytics
    Given user is a VIP program participant with a non-zero tier
    When user places a new perps order
    Then the TradeTransaction analytics event includes vip_tier and vip_discount properties

  Scenario: VIP data included in close-position analytics
    Given user is a VIP program participant with a non-zero tier
    When user closes a perps position
    Then the PositionCloseTransaction analytics event includes vip_tier and vip_discount properties

  Scenario: VIP data included in flip-position analytics
    Given user is a VIP program participant with a non-zero tier
    When user flips a perps position direction
    Then the TradeTransaction analytics event includes vip_tier and vip_discount properties
```

## **Screenshots/Recordings**

N/A — analytics-only change with no UI impact.

### **Before**

N/A

### **After**

N/A

## **Pre-merge author checklist**

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Analytics-only wiring with no trading, auth, or UI behavior changes;
risk is limited to incorrect or missing event properties if hooks return
stale values.
> 
> **Overview**
> This PR **populates VIP program fields on perps trading analytics** by
threading `vipTier` (from `useVipTier()`) and `vipDiscount` (from
`feeResults.feeDiscountPercentage`) into `trackingData` for **place
order**, **close position**, and **flip position** flows—so downstream
`TradeTransaction` / `PositionCloseTransaction` events can emit the
properties the service layer already expects.
> 
> **Flip flow:** `usePerpsFlipPosition` now accepts optional
`TrackingData` and forwards it to `flipPosition`; the confirm sheet
passes fee, market price, and VIP fields. Tests mock `useVipTier` and
assert the flip handler receives a tracking payload with `totalFee` and
`marketPrice`.
> 
> **Order view:** Only analytics-related edits (VIP fields + `vipTier`
in `handlePlaceOrder` deps); some inline comments on `trackingData` were
removed.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8c48f12. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Adds the shared Predict portfolio data/model hook for PRED-902.

This introduces `usePredictPortfolio`, a reusable portfolio-domain hook
that centralizes the Predict balance, open positions, claimable
positions, claim/deposit/withdraw handlers, loading/error state, and
derived portfolio summary values for the homepage portfolio module and
future Positions screen.

The hook derives:

- available Predict balance
- active/open positions and actionable claimable positions
- portfolio value
- open, claimable, and badge counts
- claimable amount
- total unrealized P&L amount and optional percent
- P&L visibility using the one-cent threshold
- aggregate and granular loading/error/refetch state

Unrealized P&L is calculated from open positions only using summed
current value minus summed initial value. Resolved won/lost positions
are excluded from unrealized P&L, and the P&L percent is omitted when
total initial value is zero.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Predict portfolio shared data model

  Scenario: portfolio model derives reusable values for Predict surfaces
    Given the Predict portfolio hook is rendered with balance, open positions, and claimable positions

    When the underlying balance or positions query data changes
    Then the hook returns updated portfolio value, P&L, counts, claimable amount, and loading/error state
```

Manual app QA: N/A. This PR adds a shared data/model hook and unit
coverage only; no UI integration is included.

## **Screenshots/Recordings**

N/A. No user-facing UI changes are included in this PR.

### **Before**

N/A

### **After**

N/A

## **Testing**

- `NODE_OPTIONS=--max-old-space-size=8192 node
.yarn/releases/yarn-4.14.1.cjs jest
app/components/UI/Predict/hooks/usePredictPortfolio.test.ts
app/components/UI/Predict/hooks/usePredictBalance.test.ts
app/components/UI/Predict/hooks/usePredictPositions.test.ts --runInBand
--watchman=false --forceExit --silent --coverage=false`
- `node .yarn/releases/yarn-4.14.1.cjs lint:tsc`

## **Pre-merge author checklist**

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
- [ ] I've tested with a power user scenario
- [ ] 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]
> **Low Risk**
> Data-layer hook and unit tests only; no UI or payment/auth changes,
though derived portfolio/P&amp;L logic will need careful review when
integrated.
> 
> **Overview**
> Introduces **`usePredictPortfolio`**, a shared Predict data hook that
composes balance, active/claimable positions (with live updates on open
positions), account state, and claim/deposit/withdraw actions into one
**`PredictPortfolioModel`**.
> 
> It **derives** portfolio value (balance + open position value +
actionable won claimables), open/claimable/badge counts, claimable
amount, unrealized P&amp;L from open positions only (current vs initial
value, percent omitted when initial value is zero), and P&amp;L
visibility behind a **$0.01** threshold. Won claimables with positive
value count toward value and badges; lost claimables do not. Loading,
refresh, aggregated/per-source errors, and a combined **`refetch`** are
exposed.
> 
> **Exports** the hook from the Predict hooks barrel. **Adds** unit
coverage for the portfolio hook and **new tests** that balance and
positions refetch when the selected account address changes (with more
flexible account mocks).
> 
> No UI wiring in this PR—hook and tests only.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bd8588a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## **Description**

Updated `app/core/AppConstants.ts` to stop reading
`process.env.MM_PORTFOLIO_URL` and always use the portfolio website host
directly:

- `PORTFOLIO_URL = 'https://portfolio.metamask.io'`

This ensures Browser launches from Explore cannot be redirected to an
API endpoint URL (which renders as a blank page).

## **Changelog**

CHANGELOG entry: fix: explore browser icon opens blank page.

## **Related issues**

Fixes: #29048

## **Manual testing steps**

```gherkin
Feature: Open browser from Explore

  Scenario: Browser icon opens portfolio site when no tabs are open
    Given MM app browser has no open tabs
    When user opens Explore and taps the browser icon beside search
    Then the app opens the portfolio website, not an API URL
```

## **Screenshots/Recordings**

### **Before**

Repro from bug report: browser navigates to
`https://portfolio.api.cx.metamask.io/...` and shows blank page.

### **After**

Expected navigation target resolves from `AppConstants.PORTFOLIO.URL` to
`https://portfolio.metamask.io`.

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

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

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-5e53fa06-e2dd-4725-837c-cc51d95d3374"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-5e53fa06-e2dd-4725-837c-cc51d95d3374"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
<!--
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?
-->

Update the home-screen "How it works" description to match the latest
Figma copy ([Figma node
8027-14650](https://www.figma.com/design/XKZ8hRqSn2iTiuzmlQLuYQ/Money-account?node-id=8027-14650&m=dev)).

- Before: "Deposit mUSD into your Money account and earn up to 4% APY.
Your balance is dollar-backed and ready to spend, trade, or send
anytime."
- After: "Add mUSD and earn up to 4% APY (variable). Your balance is
dollar-backed and ready to spend, trade, or send anytime."

The existing component already splits this into i18n
`description_prefix` · highlighted APY · `description_suffix`, so only
the two locale strings in `locales/languages/en.json` change.
Non-English locales come from the localization pipeline.

## **Changelog**

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

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

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

CHANGELOG entry: Updated the Money home "How it works" description copy.

## **Related issues**

Fixes:
[MUSD-843](https://consensyssoftware.atlassian.net/browse/MUSD-843)

## **Manual testing steps**

```gherkin
Feature: Money home — "How it works" copy

  Scenario: User views the updated description on the Money home screen
    Given the user has a Money account

    When user opens the Money home screen
    Then the "How it works" section reads "Add mUSD and earn up to 4% APY (variable). Your balance is dollar-backed and ready to spend, trade, or send anytime."
    And the "4% APY" segment is highlighted in the success color
```

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

[MUSD-843]:
https://consensyssoftware.atlassian.net/browse/MUSD-843?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Copy-only change in English locale strings and tests; no business
logic or security impact.
> 
> **Overview**
> Updates the Money home **How it works** copy in English to match
Figma: the prefix shifts from depositing into the Money account to **Add
mUSD and earn up to**, and the suffix now includes **(variable)** before
the dollar-backed balance sentence.
> 
> `MoneyHowItWorks` still composes prefix, highlighted APY, and suffix
from i18n—no UI logic changes. Unit tests in `MoneyHowItWorks.test.tsx`
were aligned with the new strings.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6f635b7. 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 fixes two smoke-test flakes while keeping the tests on the
normal fixture/mock paths.

For the Android swap deeplink failure, the test reached the Swap screen
with `1.0` USDC -> USDT selected, but the quote area stayed in its
loading skeleton until the fee-disclaimer assertion timed out. The
full-parameters deeplink scenario now loads
`tests/smoke/swap/withTokens.json`, matching the regular swap smoke
fixture so USDC contract and balance reads are available before the
bridge quote resolves.

For the linked stake smoke failure, the job was `stake-ios-smoke` and
the app reached wallet home, but `stake-button` never existed. The stake
test already had Accounts API mocks and the framework default
feature-flag mock is aligned with current production defaults, so the
fix keeps Detox synchronization on until the Earn CTA is visible, then
disables sync only for the animated staking flow. It also enables the
Mainnet network map used by the TokensFullView filter, uses the stable
Network Manager row selector for Localhost, and keeps fiat coverage by
asserting the staked ETH row contains the localized label, the expected
`1 ETH` amount, and a fiat balance matching a dollar-value regex. The
combined staked-row assertion lives in the TokensFullView page object,
with row selector constants centralized in
`tests/selectors/Wallet/WalletView.selectors.ts`, so the spec stays
aligned with the E2E page-object/selector guidelines.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

```gherkin
Feature: Smoke E2E fixture flakes

  Scenario: user opens a full-parameter USDC to USDT swap deeplink
    Given the iOS or Android smoke test app is launched with Anvil mainnet chain state
    And the Anvil node has preloaded ERC20 token contract and balance state
    When user opens the swap deeplink with USDC as the source token, USDT as the destination token, and amount 1000000
    Then the Swap screen displays USDC, USDT, and amount 1.0
    And the bridge quote resolves
    And the fee disclaimer and confirm swap button are visible

  Scenario: user starts staking from wallet home
    Given the stake smoke test app is launched with mocked Mainnet ETH balance and the framework default feature-flag mock
    When user logs in and wallet home renders
    Then the token-row Earn CTA is visible
    When user taps Earn and stakes 1 ETH
    Then the staking deposit activity is confirmed
    And the staked ETH asset row is visible with amount 1 ETH and a fiat balance
```

## **Screenshots/Recordings**

N/A - E2E-only test changes.

### **Before**

- Swap deeplink: Android CI recording showed the Swap screen reached
with USDC -> USDT and amount `1.0`, but the quote section remained in a
loading skeleton until the fee-disclaimer assertion timed out.
- Stake: iOS CI recording showed wallet home loading without the
token-row `stake-button`, so the Earn CTA assertion timed out.

### **After**

- Swap deeplink: local iOS prebuilt smoke run reached the quote view and
passed the full-parameters deeplink scenario.
- Stake: local iOS prebuilt smoke run reached the Earn CTA via the
existing Accounts API mocks and framework default feature-flag mock,
completed the staking flow, filtered TokensFullView to Localhost, and
verified the staked ETH row, amount, and fiat balance text matching the
wallet selector regex.

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.

## **Validation**

- `yarn lint:tsc`
- `yarn prettier --check tests/smoke/swap/swap-deeplink-smoke.spec.ts`
- `yarn eslint tests/smoke/swap/swap-deeplink-smoke.spec.ts`
- `yarn prettier --check tests/page-objects/wallet/HomeSections.ts
tests/selectors/Wallet/WalletView.selectors.ts
tests/smoke/stake/stake-action-smoke.spec.ts`
- `yarn eslint tests/page-objects/wallet/HomeSections.ts
tests/selectors/Wallet/WalletView.selectors.ts
tests/smoke/stake/stake-action-smoke.spec.ts`
- `PREBUILT_IOS_APP_PATH="$HOME/Downloads/main-e2e-MetaMask.app" yarn
test:e2e:ios:debug:run --testPathPattern
tests/smoke/swap/swap-deeplink-smoke.spec.ts --testNamePattern "navigate
to bridge view with full parameters"`
- `PREBUILT_IOS_APP_PATH="$HOME/Downloads/main-e2e-MetaMask.app" yarn
test:e2e:ios:debug:run --testPathPattern
tests/smoke/stake/stake-action-smoke.spec.ts --testNamePattern "should
be able to import stake test account with funds"`
- Re-ran the stake command after removing direct controller fixture
seeding and per-test feature-flag overrides; it still passed via the
normal mocked API/default flag paths.
- Re-ran the stake command after restoring the row-scoped fiat balance
assertion; it passed.
- Re-ran the stake command after moving the row-scoped fiat balance
assertion into the TokensFullView page object; it passed.
- Selector-only cleanup after that was validated with `yarn lint:tsc`
and focused Prettier/ESLint; Detox was not rerun for that cleanup.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to Detox smoke tests, selectors, and page objects;
no production app logic.
> 
> **Overview**
> Hardens two smoke E2E paths that were timing out in CI.
> 
> **Swap deeplink (USDC → USDT):** The full-parameters scenario now
seeds Anvil with `tests/smoke/swap/withTokens.json` (same as other swap
smokes) so ERC20 contract/balance reads finish before the bridge quote
leaves the loading skeleton.
> 
> **Stake from Actions:** The fixture enables Mainnet in the
network-enabled map; the Earn CTA is asserted **before** Detox sync is
turned off for the staking animation flow. Post-stake verification uses
`NetworkManager.tapNetwork('eip155:1')` instead of the network list
modal, and a shared **TokensFullView** page object asserts the staked
ETH row (label, `1 ETH`, and a fiat amount matching a dollar regex)
instead of a fixed USD string.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
156bd74. 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**

- Fix explore search v2 issues:
- Cryptos -> Crypto
- "View all" wont show in the loading state
- "View all" stays on the no_query state
- When there is a query:
  - "View X more" will be shown only if there are more than 3 items
  - Every section with less than 3 items does not show "View all"
- When there are no results we should show the correct empty state with
the pills
- If there are no results for all sections we do not show "We found
these results for "Btcx""

<!--
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: fix explore search v2 issues

## **Related issues**

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


## **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**
> UI and label logic in Explore search with broad test updates; no auth,
payments, or security-sensitive paths.
> 
> **Overview**
> Explore Search v2 UX fixes: **Crypto** tab copy (was “Cryptos”),
smarter **View all / View more** behavior, richer **empty states**, and
a **custom search placeholder** overlay.
> 
> **Section actions:** `getViewMoreLabel` now returns `null` when
there’s nothing beyond the 3-item cap (with an active query), so headers
hide the control instead of showing misleading “View all”. Loading
sections skip the button entirely. With **no** query, labels still use
**View all**. With a query, **View X more** appears only when counts
justify it; tokens without a server total can still fall back to **View
all**.
> 
> **Empty results:** When every section is empty, the list shows a
global “no results” message, optional **BTC/ETH/SOL** quick pills, and
only shows the “other results” line when some sections still have hits
(with a `count` in copy). Per-feed empty states keep the feed-specific
message.
> 
> **Search bar:** Interactive mode uses an empty native placeholder plus
a non-interactive overlay for alignment; tests target
`EXPLORE_VIEW_SEARCH_TEXT_INPUT` instead of placeholder text.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
906285a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#30666)

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

The pay-with bottom sheet was gated behind the
`MM_DEV_PAY_WITH_BOTTOM_SHEET` env variable via the
`isPayWithBottomSheetEnabled()` helper.

This PR removes the feature flag entirely and enables the pay-with
bottom sheet unconditionally:

1. **Removes `isPayWithBottomSheetEnabled()`** from `transaction-pay.ts`
2. **Simplifies navigation** in 4 files (`PayWithRow`, `PerpsPayRow`,
`PredictPayWithRow`, `PredictBuyWithAnyToken`) - always navigate to
`CONFIRMATION_PAY_WITH_BOTTOM_SHEET` instead of conditionally choosing
between the bottom sheet and the old modal
3. **Removes dead code** from `usePredictBalanceTokenFilter` and
`usePerpsBalanceTokenFilter` - the synthetic `HighlightedItem` rows
(Predict balance / Perps balance) that were only shown in the old modal
path are removed along with their unused hooks and imports
4. **Simplifies `PayWithModal`** - removes fiat highlighted actions
suppression logic since the bottom sheet handles fiat actions
5. **Updates 7 test files** - removes mocks for the flag, updates
navigation assertions, removes tests for old-path behavior

> **Note:** `PayWithModal` and its route (`CONFIRMATION_PAY_WITH_MODAL`)
are intentionally kept - they are still used as the "Other assets" token
picker launched from within the bottom sheet via
`usePayWithCryptoSection`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Pay-with bottom sheet

  Scenario: user opens pay-with token picker from confirmation
    Given user has a pending transaction confirmation with pay-with row

    When user taps the pay-with row
    Then the pay-with bottom sheet opens (not the old full-screen modal)

  Scenario: user opens pay-with from Perps order view
    Given user is on the Perps order view with a pay-with row

    When user taps the pay-with row
    Then the pay-with bottom sheet opens

  Scenario: user opens pay-with from Predict buy flow
    Given user is in the Predict buy-with-any-token flow

    When user taps Change Payment Method
    Then the pay-with bottom sheet opens
```

## **Screenshots/Recordings**

### **Before**

### **After**

### MUSD Conversion


https://github.com/user-attachments/assets/fefa8b80-cc81-4876-8221-29d9aed79c86

### Perps


https://github.com/user-attachments/assets/b48cd1bf-4497-464b-94f3-07daea47716b

### Predict


https://github.com/user-attachments/assets/362ad188-7724-4e6d-9844-2c80899147a6

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches payment selection navigation across confirmations, Perps, and
Predict; behavior change is broad but localized to pay-with UX, not auth
or funds signing.
> 
> **Overview**
> Removes the **`MM_DEV_PAY_WITH_BOTTOM_SHEET`** gate
(`isPayWithBottomSheetEnabled`) and makes the **pay-with bottom sheet**
the only entry path from confirmations, Perps, and Predict (always
**`CONFIRMATION_PAY_WITH_BOTTOM_SHEET`**).
> 
> **Perps** and **Predict** balance token filters no longer prepend
synthetic **Perps balance** / **Predict balance** highlighted rows or
wire **Add funds** from those lists—that UI lived on the old modal path.
Perps still applies allowlist filtering and clears other tokens’
selection when perps balance is selected.
> 
> **`PayWithModal`** stops prepending fiat highlighted actions and the
flag-based suppression; token lists come from filters only. The modal
route remains for flows such as **Other assets** from the bottom sheet.
> 
> Tests drop flag mocks and assert bottom-sheet navigation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
db8b7e4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

The Money Account confirmation screens did not match the Convert
experience used on mobile home: the Add screen showed "Add money" with
no conversion tooltip, and the Transfer screen showed "Transfer money".

This aligns those screens with MUSD-806:

- **Add → "Add funds" + conversion tooltip** — `MoneyAccountDepositInfo`
now installs its navbar through the new `useMoneyAccountDepositNavbar`
hook, which sets the "Add funds" title and adds an info button that
opens the conversion tooltip (`useMoneyAccountDepositTooltip`). The
tooltip APY is read from `useMoneyAccountBalance().apyPercent`, so the
copy stays in sync with the account's real rate.
- **Transfer → "Transfer funds"** — `MoneyAccountWithdrawInfo` title
string updated.
- **Projected balance** — the deposit amount screen shows the projected
balance via `BalanceProjection`. The copy is unified into a single
parametrised key (`confirm.custom_amount.projected_balance` → "Projected
{{projectedYears}}-year balance:") instead of the year-pinned
`projected_five_year_balance`, and the component takes a
`projectedYears` prop (currently `1`).

Heading copy lives in `en.json` (`money_account_add_money`,
`money_account_transfer_money`) alongside the new tooltip strings
(`deposit_tooltip_title`, `deposit_tooltip_description`).

**Scope:** this is the design-review heading / tooltip / projection-copy
change only. The Deposit funds / Move mUSD routing and the Transfer
destinations belong to the MM Pay / Ramps teams and are tracked
separately.

## **Changelog**

CHANGELOG entry: Updated the Money Account Add and Transfer confirmation
screens with corrected headings ("Add funds" / "Transfer funds"), a
conversion tooltip on the Add screen, and a unified parametrised
projected-balance copy.

## **Related issues**

Fixes:
[MUSD-806](https://consensyssoftware.atlassian.net/browse/MUSD-806)

## **Manual testing steps**

```gherkin
Feature: MM Pay Money Account heading, tooltip and projection

  Scenario: user opens the Add screen
    Given the user has a Money Account
    When the user starts Add money (Convert crypto)
    Then the screen heading reads "Add funds"
    And tapping the navbar info button shows the conversion tooltip
    And the tooltip APY matches the account's current rate

  Scenario: projected balance updates with the entered amount
    Given the user is on the Add screen
    When the user enters an amount
    Then a "Projected 1-year balance:" line shows the compounded amount

  Scenario: user opens the Transfer screen
    Given the user has a Money Account
    When the user starts Transfer (Between accounts)
    Then the screen heading reads "Transfer funds"
```

## **Screenshots/Recordings**

### **Before**

### **After**


https://github.com/user-attachments/assets/84acc820-5566-4a65-b722-4ff35be3fe6b


## **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
- [x] I've instrumented key operations with Sentry traces for production
performance metrics

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


[MUSD-806]:
https://consensyssoftware.atlassian.net/browse/MUSD-806?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Projection math and APY source switched from percent-based 5-year
vault data to `apyDecimal` with configurable years—user-visible amounts
can change if APY wiring is wrong; otherwise UI/copy and tests only.
> 
> **Overview**
> Aligns Money Account **Add** and **Transfer** confirmation screens
with the Convert mobile experience (MUSD-806): headings become **“Add
funds”** / **“Transfer funds”**, and the Add flow gets a navbar **info**
control that opens an APY conversion tooltip driven by
`useMoneyAccountBalance().apyPercent`.
> 
> **Add screen** no longer calls `useNavbar` directly; it uses
**`useMoneyAccountDepositNavbar`**, which sets the title, back button,
and a header info button wired to **`useMoneyAccountDepositTooltip`**
(balance hook only mounts while the modal is open).
> 
> **Projected balance** replaces the fixed **5-year**
`ProjectedFiveYearBalance` with **`BalanceProjection`**: a
`projectedYears` prop, copy via
`confirm.custom_amount.projected_balance`, and compounding on
**`apyDecimal`** instead of vault APY as a percent. Money account
deposit shows a **1-year** projection under the amount.
> 
> New **`en.json`** strings cover the deposit tooltip and updated
confirm titles; tests cover navbar, tooltip, and projection math.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3ab9509. 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: Matthew Grainger <46547583+Matt561@users.noreply.github.com>
Co-authored-by: Shane T <shane.odlum@consensys.net>
## **Description**

Splits the `push-eas-update` job into two parallel jobs, one per
platform (iOS and Android), running on separate runners. This avoids
LavaMoat serializer contention and mitigates intermittent OTA deployment
failures caused by a [known GitHub Actions runner
issue](actions/runner#3819 (comment)).

The shared EAS update steps have been extracted into a reusable
workflow.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

| Run | Platform | Result |
|-----|----------|--------|
| [iOS
dry-run](https://github.com/MetaMask/metamask-mobile/actions/runs/26117723683/job/76811308433)
| iOS | ✅ Passed — cancelled before EAS upload |
| [Android
test](https://github.com/MetaMask/metamask-mobile/actions/runs/26119164078)
| Android | ✅ Passed |
| [Both platforms in
parallel](https://github.com/MetaMask/metamask-mobile/actions/runs/26120507757)
| iOS + Android | ✅ Both passed |

## **Before/After**

### **Before**
Single `push-update` job — iOS bundled first, then Android sequentially
on the same runner.

### **After**
Two parallel jobs (`push-update-ios`, `push-update-android`) running
simultaneously on isolated runners, visible as side-by-side nodes in the
Actions run graph. Verified via dry-run dispatch targeting `exp`
channel.
## **Description**

Fixes two touch gesture issues with the TradingView chart:
1. **iOS back gesture blocked**: Users couldn't swipe from the left edge
to navigate back
2. **Vertical scroll hijacked**: Chart was capturing vertical drag
gestures, preventing page scrolling

## Changes

### 1. iOS Edge Overlay (`Price.advanced.tsx`, `Price.styles.tsx`)
- Added 15px transparent overlay on left edge (iOS only) with
`pointerEvents="box-only"`
- Prevents chart from capturing touches at screen edge, preserving
native back gesture

### 2. Disabled Vertical Touch Drag (`AdvancedChart.types.ts`)
- Added `'vert_touch_drag_scroll'` to TradingView's
`DEFAULT_DISABLED_FEATURES`
- **Fixes vertical scroll hijacking**: Chart no longer prevents page
from scrolling vertically

### 3. Removed Unused Touch Tracking (`Price.advanced.tsx`,
`Price.advanced.test.tsx`)
- Removed `usePriceChart` context integration, `handleTouchStart/End`
callbacks
- Removed touch event handlers from chart container (`onTouchStart`,
`onTouchEnd`, `onTouchCancel`)
- Removed 4 obsolete test cases for touch gesture handling
- These were intended for scroll coordination but are no longer needed

## Testing

**iOS:**
- ✅ Swipe from left edge (0-15px) triggers back navigation
- ✅ Chart interactions (pan, zoom, crosshair) work normally
- ✅ Vertical scrolling works without chart interference

**Android:**
- ✅ Chart interactions work normally  
- ✅ Vertical scrolling works without chart interference

## **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: Fixes chart interactions

## **Related issues**

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

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


https://github.com/user-attachments/assets/25a220b1-58c1-4c5d-b9c5-b04ed22f7c62




## **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**
> Touches token details chart UX and parent list scrolling; iOS-only
overlay may affect edge interactions. No auth or data-path changes.
> 
> **Overview**
> Improves **token overview advanced chart** scroll vs pan behavior by
stopping the old pattern that flipped `isChartBeingTouched` on every
chart touch (which could lock the transactions **FlatList**).
> 
> **`Price.advanced`** no longer wires `onTouchStart` / `onTouchEnd` /
`onTouchCancel` or `usePriceChart`. On **iOS**, a narrow left
**`edgeOverlay`** (`pointerEvents="box-only"`) sits over the chart so
gestures at the screen edge can reach the parent scroll view.
> 
> **TradingView** defaults now disable **`vert_touch_drag_scroll`** so
vertical drags in the WebView favor page scroll instead of chart
vertical drag.
> 
> Tests for the removed touch → context behavior are dropped.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
616a79d. 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**

Improves the crypto up/down market experience on the feed series card
and market details screen.

These changes are scoped to the existing crypto up/down work behind the
Predict up/down feature flag. They improve release quality for the
upcoming crypto up/down experience, where the current UX is unstable for
users, with minimal regression risk outside the gated feature.

This PR fixes several visible stability issues:

- Prevents Up/Down CTA prices from briefly flickering to stale fallback
prices while live CLOB price subscriptions remount or warm back up.
- Uses a shared buy-price precedence for CTA labels: live WebSocket
`bestAsk` → REST `entry.buy` → static market token price.
- Keeps the details chart anchored to the currently live market so
selecting a future time slot does not interrupt the BTC price stream.
- Keeps the selected time slot independent from the chart market, so
target price/actions can follow the selected slot while the chart
remains continuous.
- Preserves series data during transient series refetches so position
rows and chart sizing do not jump during time-slot changes.
- Keeps crypto up/down chart loading visible until the live chart has
enough renderable points, avoiding a spinner → blank → chart transition.
- Uses Polymarket event `priceToBeat` metadata as the target-line
fallback for hourly/daily crypto up/down markets whose target price API
can be unavailable.
- Preserves the existing group item threshold fallback for crypto
up/down markets that do not provide event `priceToBeat` metadata.
- Improves crypto up/down chart rendering/loading behavior and bottom
padding across larger chart heights/font scales.
- Improves longer-duration countdown/reset copy for hourly/daily/weekly
crypto up/down markets.
- Keeps crypto up/down card routing behind the Predict up/down feature
flag.

## **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: N/A

## **Manual testing steps**

```gherkin
Feature: Crypto up/down market experience

  Scenario: user views the crypto up/down series card while prices update
    Given the Predict up/down feature flag is enabled
    And a live crypto up/down market is visible in the Predict feed

    When live CLOB price updates are received for the Up and Down tokens
    Then the Up and Down CTA prices update from the live buy prices
    And the CTA prices do not briefly flicker to stale REST or static market prices

  Scenario: user opens crypto up/down market details and changes time slots
    Given the user is on a live crypto up/down market details screen
    And the market series includes a future time slot

    When user selects the future time slot
    Then the selected time slot updates
    And the chart remains anchored to the live market price stream
    And the Up and Down actions use the selected time slot market
    And the current price display remains stable while the target price loads

  Scenario: user waits for the crypto up/down chart to load
    Given the user is on a crypto up/down market details screen
    And live chart data has not produced at least two renderable points

    When the initial chart request finishes
    Then the chart loading state remains visible
    And the screen does not briefly show an empty chart area before the live line renders

  Scenario: user views hourly or daily crypto up/down target lines
    Given an hourly or daily crypto up/down market has Polymarket event price to beat metadata
    And the crypto target price API is unavailable

    When the market card or market details screen is rendered
    Then the target line uses the event price to beat metadata
    And the existing group item threshold fallback is still available if event metadata is missing

  Scenario: user views longer-duration crypto up/down markets
    Given a crypto up/down market has an hourly, daily, or weekly recurrence

    When the market card or time slot picker is rendered
    Then the countdown and reset copy use readable longer-duration formatting
```

## **Screenshots/Recordings**

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

### **Before**

N/A

### **After**

N/A

## **Pre-merge author checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches live pricing, chart anchoring, and controller fallbacks across
Predict crypto up/down flows, but scope is largely behind the up/down
feature flag with broad test coverage.
> 
> **Overview**
> This PR tightens the **gated** crypto up/down Predict experience (feed
card, details, and routing) so prices, charts, and target lines stay
stable during live updates and time-slot changes.
> 
> **Pricing & live data:** Buy CTAs now share `getPredictBuyPrice` (live
`bestAsk` → REST `entry.buy` → token price). `useLiveMarketPrices`
caches recent updates so remounts do not flash stale REST/static prices.
Chart loading stays active until at least two renderable points exist;
current price can still propagate while loading.
> 
> **Details screen:** The chart stays on the **live** series market
while the picker only changes the selected slot (target line, Up/Down
actions, share). Series markets are held in a ref during refetch so
positions/chart height do not jump. Target/current summaries use
`resolveCryptoTargetPrice` with skeletons when appropriate; chart bottom
padding scales with height and font scale.
> 
> **Target price & Polymarket:** Event `priceToBeat` is parsed onto
markets; `PredictController` and UI fall back through fetched price →
event metadata → `groupItemThreshold`. Hourly recurrence is supported in
duration helpers.
> 
> **UX polish:** Longer countdowns use `H:MM:SS` when ≥1 hour; reset
copy uses readable hour/day/week strings. Up/down feed cards require
`selectPredictUpDownEnabledFlag` in addition to `isCryptoUpDown`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
31cc179. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…are (#30622)

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

Bump for assets-controller with latest fixes for solana and non-evm
asset imports already added to extension.

No changelog as the flag is not enabled yet and the feature is not fully
implemented.

## **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/ASSETS-3270

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

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Dependency bump (assets-controller 7→8) plus changes to token hide
logic and non-EVM import paths that touch AssetsController and
MultichainAssetsController; feature-flagged unify state limits exposure
but asset state can be wrong if regressions slip in.
> 
> **Overview**
> Upgrades **`@metamask/assets-controller`** to **^8.0.1** (and
**`@metamask/assets-controllers`** to **^108.1.0**) to pick up Solana /
non-EVM fixes already on extension, and wires mobile to match unified
assets behavior.
> 
> **Hide / show tokens:** `useAssetVisibility` no longer treats “custom”
vs “has balance” as exclusive when hiding. For visible tokens it can
call **`removeCustomAsset`** and **`hideAsset`** together so a
user-added token that also has a balance entry does not stay visible
after hide.
> 
> **Add token search:** With **assets unify** enabled, non-EVM imports
still use **`MultichainAssetsController.addAssets`**, but now also
register each asset via **`AssetsController.addCustomAsset`** (same
pattern as EVM), with tests updated.
> 
> **Engine messenger:** **`getAssetsControllerMessenger`** uses the
package **`AssetsControllerMessenger`** action/event types, drops the
large local allow-list and unsafe cast, and delegates
**`SnapController:snapInstalled`**. Init imports the messenger type from
**`@metamask/assets-controller`** instead of the local re-export.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9c6ae62. 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: salimtb <salim.toubal@consensys.net>
Kureev and others added 3 commits May 27, 2026 16:43
## **Description**

Updates the Money account legal copy to align with the latest Figma
source for MUSD-833. Touches:

- Onboarding animation disclaimer footer on all four steps
(`step1`–`step4_footer_text`).
- "How it works" body copy on both the home preview
(`how_it_works.description_*`) and the dedicated How It Works view
(`how_it_works_page.description_1`), including the verb change `Deposit
→ Add`.
- "What you get" benefit row — appends `(variable)` next to the APY
value, gated by `isPositiveNumber(apy)` so it never renders orphaned.
- "Earn on your crypto" tooltip sheet wording
(`earn_crypto_info_sheet.body`).
- Link MetaMask Card subtitle — adds `(variable)` next to APY in
`card.link_subtitle` (bullet `card.link_bullet_apy` intentionally
unchanged to match Figma).
- `MoneyOnboardingView` wiring: surfaces the new `step3_footer_text` /
`step4_footer_text` strings on the onboarding step config.

Copy-only and `en.json`-only — non-English locales come from the
localization pipeline.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:
[MUSD-833](https://consensyssoftware.atlassian.net/browse/MUSD-833)

## **Manual testing steps**

```gherkin
Feature: Money account legal copy

  Scenario: User sees the updated disclaimer on each onboarding step
    Given the Money account onboarding has not been seen
    When the user advances through the 4 onboarding steps
    Then step 1 shows "APY is variable and not guaranteed."
    And step 2 shows "Money account uses Veda infrastructure."
    And step 3 shows "Includes Steakhouse Financial monitoring."
    And step 4 shows "Powered by Monad."

  Scenario: User views the updated How it works copy on Money home
    Given the user has a Money account with a positive APY
    When the user opens the Money home view
    Then the How it works section reads "Add mUSD to your Money account and earn up to {APY}% APY (variable). Your balance is dollar-backed and ready to spend, trade, or send anytime."

  Scenario: User views the Earn on your crypto tooltip
    Given the user is on the Money home view
    When the user taps the Earn on your crypto info icon
    Then the bottom sheet body reads "Illustration assumes {APY}% Annual Percentage Yield (APY) remains unchanged for one year. APY is variable and is not guaranteed. No guarantee of return."

  Scenario: User views the dedicated How it works view
    Given the user is on the Money home view
    When the user taps into the How it works section
    Then description_1 reads "Add mUSD into your Money account and earn up to {APY}% APY (variable) automatically. Funds go into a DeFi vault, administered by Veda with risk monitoring by Steakhouse Financial. The rate is generated by third-party DeFi platforms that deploy funds in blockchain protocols. No staking, no claiming, no lock-ups."

  Scenario: User views the Link MetaMask Card section
    Given the user has not yet linked a MetaMask Card
    When the user views the Link MetaMask Card section on Money home
    Then the subtitle includes "up to {APY}% APY (variable) on your balance"
    And the "Earn up to {APY}% APY" bullet stays unchanged (matches Figma)
```

## **Screenshots/Recordings**

### **Before**

### **After**

## **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
- [ ] I've tested with a power user scenario
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics

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


[MUSD-833]:
https://consensyssoftware.atlassian.net/browse/MUSD-833?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Copy and conditional text rendering only; no changes to balances,
vault logic, or authentication.
> 
> **Overview**
> Updates **Money account** user-facing legal and marketing copy in
**`en.json`** (MUSD-833): onboarding step footers on all four steps,
**How it works** home and full-page text (**Deposit → Add**,
Veda/Steakhouse/DeFi disclaimers), **Earn on your crypto** tooltip, and
**Link MetaMask Card** subtitle with **(variable)** next to APY.
> 
> **UI behavior** changes alongside copy: **`MoneyOnboardingView`**
shows new **`step3_footer_text`** / **`step4_footer_text`**;
**`MoneyHowItWorks`** only shows the highlighted APY block when APY is a
**positive** number (not zero/loading/missing), otherwise
**`description_no_apy`**; **`MoneyWhatYouGet`** appends **(variable)**
next to the APY label only when **`isPositiveNumber(apy)`** so it is not
orphaned.
> 
> Tests are updated to match the new strings and APY display rules.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a257c53. 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: Matthew Grainger <46547583+Matt561@users.noreply.github.com>
Co-authored-by: Shane T <shane.odlum@consensys.net>
…ask/perps-controller (#30507)

## **Description**

The perps controller source of truth moved to the core monorepo as
`@metamask/perps-controller` ([core PR
#8871](MetaMask/core#8871)). This PR removes the
in-tree copy and switches mobile to consume the published npm package.

Changes:
- Delete `app/controllers/perps/` — all source, tests, types, utils
(~82k lines)
- Remove local aliases from `tsconfig.json`, `babel.config.js`,
`metro.config.js`
- Add `@metamask/perps-controller@^6.3.0` to `package.json`
- Add `tsconfig` path mappings for subpath imports (`constants/*`,
`types`, `utils/*`) — needed until mobile moves to `Node16` module
resolution
- Stub `MYXProvider` in Metro resolver — the provider is excluded from
the published dist (extension-only); babel's `dynamicImportToRequire`
rewrites the dynamic `import()` to `require()`, causing Metro to resolve
it statically
- Widen cronjob slice storage type to `Record<string, unknown>` — breaks
a `TS2589` deep-recursion cycle between `@metamask/snaps-controllers`'
`Json` types and reduxjs-toolkit's `WritableDraft` mapper when the
published perps-controller types are in scope

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: TAT-3187

## **Manual testing steps**

```gherkin
Feature: Perps trading after controller migration

  Scenario: app boots and perps screens render
    Given mobile depends on @metamask/perps-controller@^6.3.0 from npm
    When the user opens the Perps tab
    Then markets, positions, orders, and account state render without errors

  Scenario: place a trade end-to-end
    Given the user is on testnet with a funded account
    When the user opens a market position on BTC
    Then the order submits and the position appears in the positions list
```

## **Screenshots/Recordings**

### **Before**

N/A — refactor with no visual surface change.

### **After**

N/A — refactor with no visual surface change.

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

#### Performance checks (if applicable)

- [ ] I've tested on Android
- [ ] I've tested with a power user scenario
- [ ] 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**
> Touches lint/import policy and removes generated controller action
types tied to perps trading; runtime behavior depends on the full npm
migration elsewhere in the PR.
> 
> **Overview**
> Removes **in-tree Perps controller lint and import fences** now that
mobile consumes **`@metamask/perps-controller`** from npm instead of
`app/controllers/perps/`.
> 
> **ESLint (`.eslintrc.js`):** Drops the Core-alignment override that
targeted `app/controllers/perps/**` and perps `*-method-action-types`
(keeps the generic `app/**/*-method-action-types*` rule). Removes the
perps test `no-duplicate-imports` exception and all
**`no-restricted-imports`** patterns that blocked relative imports into
`**/controllers/perps/**`, plus perps exclusions from the default
import-fence override.
> 
> **Deleted file:** Removes the auto-generated
**`PerpsController-method-action-types.ts`** (~1k lines) from the repo;
messenger action types for `PerpsController` now come from the published
package (aligned with the broader PR that deletes
`app/controllers/perps/` and wires Engine/UI to
`@metamask/perps-controller@^6.3.0`).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d23cd7b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Reverts the architecture-narrowing changes from #30590 and #30642 that
were rushed in to silence Google Play's 16 KB page-size warning for
`base/lib/x86_64/libconceal.so` and `base/lib/x86_64/libsecp256k1.so`.
Google Play **extended their compliance deadline**, so we have time to
fix the underlying 4 KB alignment issue properly instead of shipping
fewer ABIs as a workaround.

The structural fixes are tracked separately:

- #30591 — replace `react-native-fast-crypto` with
`react-native-quick-crypto` scrypt (drops `libsecp256k1.so`).
- #30592 — drop the Facebook Conceal dependency from
`react-native-keychain` (drops `libconceal.so`).

Once those land, both `.so` files disappear for **every** ABI (including
arm64-v8a), which is the real fix Play actually cares about.

### What this PR restores

| File | After this PR (= pre-#30590 state) |
|---|---|
| `android/gradle.properties.release` |
`reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64` |
| `android/app/build.gradle` | `reactNativeArchitectures()` back to its
original one-liner; `ndk.abiFilters(*reactNativeArchitectures())` block
removed from `defaultConfig` |

### Why revert rather than keep `abiFilters` for safety?

The `abiFilters` plumbing isn't harmful when paired with all four ABIs
(it would just resolve to all four), but keeping it adds non-obvious
build behavior tied to a workaround we're abandoning. Cleaner to fully
revert and re-introduce when (if) we narrow ABIs again as a deliberate
decision.

### Untouched (already untouched by the original PRs)

- `android/gradle.properties` (default, local dev)
- `android/gradle.properties.github` (CI E2E)
- `android/gradle.properties.github.dual-versions` (BrowserStack
real-device runs)
- `scripts/build.sh` `-PreactNativeArchitectures=...` CLI overrides

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Reverts: #30590, #30642
Tracking the proper fixes: #30591, #30592

## **Manual testing steps**

```gherkin
Feature: Production AAB ships all four ABIs again

  Scenario: A production release build is generated
    Given a clean checkout of this branch
    When the production AAB is built via the standard release workflow
      (cp android/gradle.properties.release android/gradle.properties
       before ./gradlew bundleProdRelease)
    Then `unzip -l app-prod-release.aab | grep '/lib/'` shows
      `lib/arm64-v8a/`, `lib/armeabi-v7a/`, `lib/x86/`, and `lib/x86_64/`
      entries

  Scenario: Local dev unchanged
    Given default android/gradle.properties (unchanged)
    When `yarn android` is run on an x86_64 emulator
    Then app installs and runs as before

  Scenario: CI E2E unchanged
    Given android/gradle.properties.github overlay
    When E2E build runs
    Then x86_64 APK is produced and Detox tests pass
```

## **Screenshots/Recordings**

### **Before**

Production AAB ships only `armeabi-v7a` + `arm64-v8a`. Play 16 KB
warning cleared (workaround), but the underlying 4 KB-aligned `.so`
files still ship for arm64-v8a.

### **After**

Production AAB ships all four ABIs again, restoring full device coverage
(including Chromebook ARC++ on x86_64). Play 16 KB warning returns for
x86_64, but the warning is informational under the extended deadline and
the structural fixes (#30591, #30592) are the real path forward.

## **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
- [ ] I've included tests if applicable — N/A (build-config revert;
verification is via the `unzip -l` check on the produced AAB)
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable — N/A
- [ ] I've applied the right labels on the PR — `team-mobile-platform`

#### Performance checks (if applicable)

- [x] I've tested on Android — restores the pre-#30590 known-good
behavior used in production for the prior release.
- [ ] I've tested with a power user scenario — N/A (no JS behavior
change)
- [ ] I've instrumented key operations with Sentry traces — N/A

## **Pre-merge reviewer checklist**

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes only release packaging/architecture selection but increases
shipped native surface (x86/x86_64) and may re-expose Play 16 KB
alignment warnings until dependency fixes land.
> 
> **Overview**
> Reverts the Play Store **16 KB page-size workaround** that limited
production native builds to ARM only. **Release**
`gradle.properties.release` again sets `reactNativeArchitectures` to all
four ABIs (`armeabi-v7a`, `arm64-v8a`, `x86`, `x86_64`), so production
AABs regain x86/x86_64 coverage (e.g. Chromebooks).
> 
> In `app/build.gradle`, the
**`ndk.abiFilters(*reactNativeArchitectures())`** block is removed from
`defaultConfig`, so packaging no longer forces ABI filtering at the NDK
layer. `reactNativeArchitectures()` is collapsed back to a single
ternary that reads the property or falls back to the four default ABIs.
> 
> **Trade-off:** Google Play’s x86_64 16 KB alignment warning may return
until follow-up work replaces misaligned third-party `.so` files; that
is intentional per the extended compliance timeline.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
61f5095. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Cursor <cursoragent@cursor.com>
@pull pull Bot merged commit a81cae3 into Reality2byte:main May 27, 2026
1 check failed
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.