release: 8.7.0 - #34442
Conversation
<!--
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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
<!--
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?
-->
Polymarket actively covers additional soccer, baseball, hockey, and
American football leagues whose markets currently appear in the Predict
feed without the curated game-page experience. This change expands
`predictExtendedSportsMarkets` to include those leagues so supported
game events open with grouped markets.
The app-side sports capability list and slug parser now support the
eleven leagues that were not already recognized by the client, including
their provider-defined home/away ordering. The `predictLiveSports`
production test configuration is expanded for those leagues as well
because game construction and team loading are prerequisites for
extended market grouping.
Leagues added to `predictExtendedSportsMarkets`:
- Soccer: `uel`, `col`, `fl1`, `ere`, `bra`, `por`, `bel1`, `elc`, `lib`
- Baseball: `kbo`, `npb`, `cpbl`
- Hockey: `shl`, `khl`, `cehl`, `dehl`
- American football: `nfl`, `cfb`, `cfl`
NFL's standard game-line markets are supported by this change. NFL
player props such as touchdowns and yardage remain outside the flag's
enabled market types and are not part of this scope.
As a temporary compatibility guard, directly opened child events remain
on their existing generic details page whenever any active child market
cannot be represented by the currently enabled grouped market types.
Fully groupable children and parent game cards continue to use the
curated page. This guard can be removed once all relevant sports market
types are supported.
Legacy Predict Appium flows explicitly disable both the `7.82.0` and
`8.6.0` extended-sports variations through a shared helper. This
prevents deep-merged production defaults from unintentionally changing
cash-out, claim, geo-restriction, open-position, and withdrawal smoke
scenarios.
CFB, NFL, and NBA first-half moneyline markets are enabled under a
dedicated `First Half` group. New league and market-type configuration
is versioned at `8.6.0`; existing `7.79.0` and `7.82.0` payloads remain
unchanged for older clients.
Provider data note: two historical FL1 Nice/Saint-Etienne playoff events
use `nic` in the event slug while the embedded team and `/teams`
endpoint use `ogc` for OGC Nice. This has been isolated for confirmation
with Polymarket before adding a client-side alias.
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry: Added curated Predict game pages for more soccer,
baseball, hockey, and football leagues
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/PRED-1139
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```gherkin
Feature: Extended Predict sports market coverage
Scenario Outline: user opens a newly enabled sports game
Given predictLiveSports includes "<league>"
And predictExtendedSportsMarkets includes "<league>"
And Polymarket returns an active "<league>" game with game and team metadata
When the user opens the game card from the Predict feed
Then the curated game details page opens
And supported markets are grouped by market type
Examples:
| league |
| uel |
| col |
| fl1 |
| ere |
| bra |
| por |
| bel1 |
| elc |
| lib |
| kbo |
| npb |
| cpbl |
| shl |
| khl |
| cehl |
| dehl |
| nfl |
| cfb |
| cfl |
Scenario: user opens a previously enabled sports game
Given the existing extended sports leagues remain enabled
When the user opens an existing supported game card from the Predict feed
Then the curated game details page opens
And its supported markets remain grouped by market type
Scenario: user directly opens a child event containing an ungroupable market
Given the child event contains at least one active market type that is not enabled for grouping
When the user opens that child event from search or a direct link
Then the original child event opens on the generic market details page
And its active markets remain accessible
```
Automated validation:
- Nine focused Predict and feature-flag suites covering provider
resolution, parsing, grouping, rendering, flags, league support, and
upstream esports integration (554 tests passed)
- Android Predict Appium smoke discovery (7 tests listed successfully)
- `yarn lint:tsc`
- Targeted ESLint and Prettier checks for all changed files
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
N/A. This change expands configuration and league parsing without
introducing a new static UI; several covered leagues are currently out
of season.
### **After**
N/A. The resulting game page uses the existing curated sports UI and
depends on active Polymarket events for each league.
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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
- N/A. This is a platform-independent feature flag and parsing change
with no Android-specific code.
- [x] I've tested with a power user scenario
- N/A. League parsing and market grouping do not depend on wallet size,
account count, or token count.
- [x] I've instrumented key operations with Sentry traces for production
performance metrics
- N/A. This change adds no new runtime operation or
performance-sensitive execution path.
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 feature-flag defaults, league parsing, and Polymarket event
resolution paths that affect which details UI users see; scope is large
but covered by extensive unit tests and smoke flag isolation.
>
> **Overview**
> Expands **Predict** curated sports game pages to many additional
leagues (soccer, baseball, hockey, American football) by extending
`PredictSportsLeague`, `SUPPORTED_SPORTS_LEAGUES`, `LEAGUE_SLUG_CONFIGS`
slug/tag parsing, and draw-capable league sets.
>
> Adds **`8.6.0`** production defaults for `predictLiveSports` and
`predictExtendedSportsMarkets` (league lists plus richer
`enabledSportsMarketTypes`), while keeping older `7.79.0` / `7.82.0`
payloads unchanged.
>
> Enables **`first_half_moneyline`** as a supported/grouped market type
(mapped to a **First Half** outcome group, separate from game lines) and
treats it as moneyline-like in sports constants.
>
> Changes **Polymarket** market-details resolution: child events with a
parent are **not** merged into the parent when any **active** market
type is outside the enabled grouping set (`#canGroupAllActiveMarkets`),
so direct links keep the generic child page until all active markets are
groupable.
>
> Centralizes **Appium** smoke disabling of extended sports via
`remoteFeatureFlagExtendedSportsMarketsDisabledForPredictSmoke` (both
`8.6.0` and `7.82.0` variations).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c9c9fd3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…s cp-8.6.0 (#34029) ## **Description** Newly onboarded users saw the wallet-home onboarding checklist and the Money card at the same time, giving them two competing primary CTAs. Per the ticket (approved by Johann), the Money card is now hidden while the checklist is on screen so the checklist owns the action. The card is gated on the same selector the checklist tile already reads — `selectShouldShowWalletHomeOnboardingSteps`, surfaced in this view as the existing `inWalletHomePostOnboardingFlow` — so the two can't drift apart. That also satisfies the second acceptance criterion for free: dismissing or completing the checklist flips the selector, and the card reappears in the same render as the balance row that `AccountGroupBalance` swaps back in. No separate dismissal wiring was needed. Deliberately scoped to the card. The header's activity button keeps using `isMoneyAccountVisible` and is unaffected — only the surface underneath the checklist was in conflict. `MoneyBalanceCard` has a single render site (`app/components/Views/Wallet/index.tsx`), so this is a one-line gate plus the derived flag. Side benefit: `MoneyBalanceCard` fires a "component viewed" analytics event on mount, so it no longer reports views to users who never actually see the card. ## **Changelog** CHANGELOG entry: Hid the Money card on the wallet home screen while the onboarding checklist is showing, so new users have a single clear next step ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-968 ## **Manual testing steps** ```gherkin Feature: Money card visibility during wallet-home onboarding Scenario: newly onboarded user sees only the checklist Given the Money account flag is on and the user is geo-eligible And the user has just completed onboarding so the wallet-home checklist is showing When user views the wallet home screen Then the onboarding checklist is visible And the Money card is not rendered below it Scenario: user dismisses the checklist Given the wallet-home onboarding checklist is showing When user dismisses the checklist Then the account balance is shown And the Money card appears below it Scenario: user completes the checklist Given the user is on the last checklist step When user completes the flow Then the balance and the Money card both appear after the exit animation Scenario: existing user is unaffected Given the user is not eligible for the wallet-home checklist And the Money account flag is on and the user is geo-eligible When user views the wallet home screen Then the Money card is visible as before ``` ## **Screenshots/Recordings** https://github.com/user-attachments/assets/833ba151-9ad6-435d-9b56-efaf723f61e0 ### **Before** <img width="750px" alt="Simulator Screenshot - iPhone 17 Pro - 2026-07-29 at 20 45 37" src="https://github.com/user-attachments/assets/0c683e00-533b-4bbe-89d4-ec6250b8bdc9" /> ### **After** <img width="750px" alt="Simulator Screenshot - iPhone 17 Pro - 2026-07-29 at 20 48 36" src="https://github.com/user-attachments/assets/5556368f-a16e-4097-8c98-b27d4b820ea3" /> ## **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** - [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** > Scoped UI visibility change on wallet home with no auth, payments, or data-model changes; behavior is covered by new component-view tests. > > **Overview** > **Wallet home** no longer shows the **Money balance card** while the post-onboarding checklist is active, so new users aren’t offered two competing primary actions. > > Rendering now uses a **`showMoneyBalanceCard`** flag: Money must be enabled and geo-eligible, and the user must **not** be in the wallet-home post-onboarding flow (`inWalletHomePostOnboardingFlow`, same signal as the checklist). Dismissing or finishing the checklist flips that flag and the card can mount again in the same render cycle as the normal balance UI. > > **Component-view tests** cover the card hidden with the checklist visible, and visible again after skip on the last step. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d8d1877. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…show Pay fees cp-8.6.0 (#34026) ## **Description** Two defects on the Predict funding flow when the activity redesign is enabled. **1. "Track" opened the legacy details screen.** The funding toast's Track button routed to `Routes.TRANSACTION_DETAILS` (the confirmations team's screen) unconditionally. The activity list already sends that same local row to the redesigned `Routes.ACTIVITY_DETAILS` when `selectIsTransactionsRedesignEnabled` is on, so the toast was the only entry point that disagreed — the same transaction rendered two different ways depending on how you reached it. `navigateToTransactionDetails` now accepts `isTransactionsRedesignEnabled` + `chainId` and navigates to `ACTIVITY_DETAILS` when both are present. The flag is passed in by the caller rather than read from the store, so this util stays store-free (it lives in `app/util`, where importing the store risks an import cycle). Callers that don't pass the new options are unaffected, so the Perps toast keeps its current destination. **2. The redesigned funding details had no fee rows.** MetaMask Pay records its already-aggregated fiat fees on the `TransactionMeta` (`metamaskPay.networkFeeFiat` / `bridgeFeeFiat` / `totalFiat`). The activity adapter's token-denominated `data.fees` is empty for these rows, so the shared `ActivityDetailsFeesAndTotal` had nothing to render. A new `ActivityDetailsPayFees` section reads `metamaskPay` and renders Network fee / Bridge fee / Total using the redesign's own `ActivityDetailRow` primitives, formatted as USD via `useFiatFormatter({ currency: 'usd' })` — the same source and currency the legacy rows use, so the two screens can't disagree. A recorded zero still renders (a sponsored network fee shows `$0`); absent values omit their row. **Scope:** deposits only. Withdrawals relabel these rows ("Provider fee" / "Received total" off `targetFiat`) and have no redesigned copy in the `activity_details` locale namespace yet — this matches the existing, deliberately deposit-only step timeline in the same template. Broader `mmPay` coverage (Perps and Money-account deposits/withdrawals) needs adapter work, since `local-transaction.ts` only special-cases the Predict types today; worth a follow-up ticket. **Known limitation:** `metamaskPay` lives only on the local `TransactionMeta`, which `TransactionController#trimTransactionsForState` eventually trims. Once trimmed, the row resolves from the indexed API copy and the fee section renders nothing. It degrades to absent rather than wrong, and the legacy screen is worse in the same situation (blank body, since `useTransactionDetails` returns undefined). Pre-existing platform constraint, not introduced here. ## **Changelog** CHANGELOG entry: Fixed the Predictions "Add funds" activity details to show network fee, bridge fee and total, and to open the redesigned details screen from the funding notification ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-1215 ## **Manual testing steps** ```gherkin Feature: Predict funding activity details Scenario: user tracks a Predictions deposit from the funding toast Given the activity redesign feature flags are enabled And the user has added funds to their Predictions account When user taps "Track" on the "Adding funds" toast Then the redesigned activity details screen opens And the screen shows Network fee, Bridge fee and Total rows And tapping back returns to the Predictions-filtered activity list Scenario: user opens the same deposit from the activity list Given the activity redesign feature flags are enabled When user opens Wallet then Activity And user taps the "Add funds" Predictions row Then the same redesigned details screen opens with identical fee values Scenario: fee values match the legacy screen Given the activity redesign feature flags are disabled When user taps "Track" on the "Adding funds" toast Then the legacy details screen opens And its Network fee, Bridge fee and Total match the redesigned screen ``` ## **Screenshots/Recordings** https://github.com/user-attachments/assets/22356882-4ca0-4176-8a47-50ff3d7b4cc4 ### **Before** https://github.com/user-attachments/assets/1ca0fc3e-7d5f-48b9-b272-0c37e243d6a4 ### **After** https://github.com/user-attachments/assets/22356882-4ca0-4176-8a47-50ff3d7b4cc4 ## **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** - [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** > UI and navigation changes behind an existing feature flag, with legacy fallback when chain id is missing; fee display reads existing Pay metadata without changing payment logic. > > **Overview** > Aligns Predict funding **Track** navigation with the activity redesign and surfaces **MetaMask Pay** fee rows on the redesigned deposit details screen. > > **Track navigation:** `navigateToTransactionDetails` now accepts `isTransactionsRedesignEnabled` and a CAIP-2 `chainId`. When both are set, it opens `ACTIVITY_DETAILS` instead of legacy `TRANSACTION_DETAILS`; without `chainId` it still falls back to legacy. Predict deposit toasts pass the redesign flag and chain id from transaction metadata (`toEvmCaipChainId`). > > **Pay fees on redesigned details:** New `ActivityDetailsPayFees` reads `metamaskPay` from local transaction meta and renders network fee, bridge fee, and total in **USD** (even if the wallet display currency is not USD), with token/network badges via extracted `ActivityFeeTokenValue`. Deposit **Predict** funding template shows this section above the step timeline when Pay fiat metadata exists; zero fees still display (e.g. sponsored `$0`). > > Tests cover navigation branches, Pay fee rendering edge cases, and Predict details integration. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 92bc0dc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** `PriceChart` was recomputing `prices.map(...)` into `priceList` on every render. Chart scrubbing updates `positionX` continuously, so that map over hundreds of points ran on every touch frame and also handed a fresh array identity into the stablecoin Y-axis `useMemo`, defeating its cache. This wraps the derivation in `useMemo` keyed on `prices`, so scrub re-renders reuse the same `priceList` and the downstream stablecoin range memo stays valid. Upstream `prices` is already referentially stable (`useState` in `useTokenHistoricalPrices`, and `distributedPriceData` is memoized in `Price.legacy`). ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ASSETS-3723 Refs: #31272 ## **Manual testing steps** N/A — behavior-preserving performance change. Existing `PriceChart` unit tests cover empty/loading/stablecoin/scrub paths. Optional smoke: open token overview → scrub chart across 1D/1W/1M → confirm line, end-dot, and scrub tooltip still update correctly. ## **Screenshots/Recordings** N/A — no UI/behavior change. ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### 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. ### Changed files - `app/components/UI/AssetOverview/PriceChart/PriceChart.tsx` — memoize `priceList` with `useMemo(..., [prices])` - Verified: `npx eslint` (warnings only, pre-existing), `yarn prettier --check`, `yarn jest --coverage=false PriceChart.test.tsx` (25/25), `yarn lint:tsc` <div><a href="https://cursor.com/agents/bc-0ae13633-4418-407c-81fd-a66ccdbee10b"><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> <a href="https://cursor.com/automations/ad0b477a-8482-11f1-a7d1-d6b4613131ce"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/view-automation-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/view-automation-light.png"><img alt="View Automation" width="141" height="28" src="https://cursor.com/assets/images/view-automation-dark.png"></picture></a> </div> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Behavior-preserving React memoization in chart rendering only; no API, auth, or data-handling changes. > > **Overview** > **PriceChart** no longer rebuilds `priceList` on every render. The `prices → numeric series` map is wrapped in `useMemo` with `[prices]` as the dependency. > > Chart scrubbing updates `positionX` on each touch frame, which previously re-ran that map over the full series and passed a new array into the stablecoin Y-axis `useMemo`, forcing extra sort/range work during gestures. When `prices` is unchanged, scrub re-renders now reuse the same `priceList` reference and keep that downstream memo valid. **No chart behavior or UI change**—render-path optimization only. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a1febd4. 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 Agent <cursoragent@cursor.com> Co-authored-by: prithpal.sooriya <prithpal.sooriya@consensys.net>
) ## **Description** <!-- mms-check: type=text required=true --> <!-- 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 continues work on the Perps Pro market detail screen, adding a new account balance bottom sheet and polishing several existing pieces of the header, order form, order book, and positions panel so they line up with Figma. - **Account balance bottom sheet**: tapping the wallet icon in the Pro header now opens a `PerpsBalanceBottomSheet` showing total balance, available balance, and unrealized P&L, with quick access to Withdraw / Add funds and Perps activity — without leaving the market screen. - **Header polish**: the market identity/price row now crossfades into a compact `[Ticker]-[collateral] perp` subtitle vs. a live price display as the user scrolls, and `PerpsMarketIdentity` was adjusted to match. - **Order book & chart collapse animation**: the order-book column and the chart panel now animate in/out (fade + layout transition) when collapsed/expanded instead of abruptly mounting/unmounting. - **Pro order form slider**: the design system `Slider` has no small/compact size variant, so the "Size (USD)" slider in the Pro order form is now visually shrunk (via a scale-transform wrapper) to match Figma's small slider spec, while preserving its full touch target. Also added the top spacing above the slider and made the "Size (USD)" label tappable to focus the input and open the keyboard, both to match Figma. - Misc smaller polish to `PerpsProOrderCard`, `PerpsProPositionsPanel`, `PerpsProUnrealizedPnl`, and `LivePriceHeader`. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Added an account balance bottom sheet to the Perps Pro market screen and polished its header, order form, and order book/chart animations ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Perps Pro market view polish Background: Given I am logged into MetaMask Mobile And I have opened a Perps Pro market detail screen Scenario: user opens the account balance bottom sheet from the header When user taps the wallet icon in the Pro market header Then a bottom sheet should open showing total balance, available balance, and unrealized P&L And the sheet should offer Withdraw and Add funds actions And tapping outside the sheet should close it without leaving the market screen Scenario: header subtitle crossfades into the live price on scroll Given the market screen is scrolled to the top, showing the price section When user scrolls down past the price section Then the header should crossfade from the "[Ticker]-[collateral] perp" subtitle to a compact live price with 24h change When user scrolls back to the top Then the header should crossfade back to the subtitle Scenario: order book and chart collapse/expand with animation Given the order book column and chart panel are both expanded When user collapses the order book Then the order book column should fade/animate out and the order form should smoothly resize to fill the row When user expands the order book again Then it should fade/animate back in When user collapses the chart panel Then the chart should animate out and the panels below it should slide up into place When user expands the chart again Then it should animate back in Scenario: Pro order form Size (USD) slider matches Figma Given user is on the Pro order form with the "Size (USD)" input visible Then the size slider should appear visually small (thumb/track/dots matching Figma), with adequate spacing above it When user taps the "Size (USD)" label Then the numeric keyboard should open and the input should be focused When user drags the slider thumb near its edges Then the drag should register normally, without a smaller-than-expected touch target ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** N/A ### **After** https://github.com/user-attachments/assets/61951ed5-bec8-4308-bc52-795a47f47b0c ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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`. --> - [ ] 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 live balance/add-funds/withdraw flows and geo-eligibility in the new sheet, plus scroll-linked header animation and market data enrichment—mostly UI but with real money-adjacent actions. > > **Overview** > Adds **`PerpsBalanceBottomSheet`** so the Pro header wallet control opens balance, available funds, P&L, withdraw/add funds, and activity **on the market screen** instead of navigating to Perps home. > > The Pro header gains **scroll-driven crossfade** from the perp-pair subtitle to a compact live price (`useHeaderStandardAnimated`, `LivePriceHeader` **`prominent`** size). **`PerpsMarketIdentity`** accepts custom **`subtitleContent`** for that row. > > **`usePerpsMarkets`** enriches route markets missing formatted **`maxLeverage`** (deep links / minimal params), with **`skipInitialFetch`** when leverage is already present. > > **Layout & motion:** `ScrollView` → **`Animated.ScrollView`**; chart expand/collapse and order-book column use **fade + `LinearTransition`**. Order-book **expand** moves to the order form (**book icon** beside long/short). Order-book **buy/sell-only toggle** is hidden; depth bars animate on ticks. > > **Order form UX:** compact **`PerpsSlider`** via scale wrapper; **Size (USD)** label focuses input; shared **`px-2`** inset via layout. Wallet handling removed from **`usePerpsProMarketHeaderActions`** in favor of local sheet state. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 096f7b4. 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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
Bump `@metamask/money-account-balance-service` to `^2.4.0`. Needed to
convert Veda API's APR to APY.
<!--
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**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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: update @metamask/money-account-balance-service to
^2.4.0
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: [MUSD-1229: Update money-acount-balance-service in Mobile app +
Ensure we're not compounding clientside
already](https://consensyssoftware.atlassian.net/browse/MUSD-1229)
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
N/A - package bump
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
N/A - package bump
### **After**
<!-- [screenshots/recordings] -->
N/A - package bump
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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`.
-->
- [ ] 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 how Money account yield (APY) is derived for user-facing
balances and copy, with no in-repo code review of the package
diff—incorrect rates would be a product/trust issue rather than an auth
or data breach.
>
> **Overview**
> Updates **`@metamask/money-account-balance-service`** from **^2.3.0**
to **^2.4.0** in `package.json` and refreshes **`yarn.lock`**. The
resolved **2.4.0** package now pulls
**`@metamask/money-account-api-data-service` ^0.4.0** as a transitive
dependency (replacing the older **^0.2.0** tree entry in the lockfile).
>
> There are **no application source changes** in this PR—Money UI
continues to consume balances/APY via existing hooks such as
`useMoneyAccountBalance`. The bump is intended to pick up library
behavior for **Veda API APR→APY** handling and to avoid
**double-compounding yield on the client** (per MUSD-1229).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c0606cf. 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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
<!--
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?
-->
Sets up feature flag for DeFi positions v2 and initialises new DeFi
controller.
The old controller remains the same, but is disabled when the new
feature flag is enabled.
The new controller is only enabled when the new feature flag is enabled.
The old feature flag has no impact.
There is a follow up PR with all the UI work needed to display the state
from the new controller, and there is a task to remove the old flag and
old controller when the new one becomes stable in prod.
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-3695
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```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**
<!-- mms-check: type=screenshot required=true -->
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
Should not display any changes, as it just initialises controller and
the FF is off.
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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
- [ ] 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 Engine composition and mutually exclusive DeFi data paths
behind a flag (default off); mis-flagging could disable positions or run
the wrong controller, but rollout is controlled and well-tested at
init/selector level.
>
> **Overview**
> Introduces **`DeFiPositionsControllerV2`** into the mobile Engine
alongside the existing DeFi positions controller, gated by a new remote
feature flag **`defiControllerV2`** (default off).
>
> **V2 init** wires the `@metamask/assets-controllers` controller with a
cached **`createApiPlatformClient`** (bearer token via
`AuthenticationController`, currency from `AssetsController`). It runs
only when basic functionality, completed onboarding, and the V2 flag are
all true.
>
> **V1** `isEnabled` now also requires
**`!selectDefiControllerV2Enabled`**, so only one controller fetches
positions at a time. Homepage selectors split the same way:
**`selectDeFiPositionsSectionEnabled`** turns off when V2 is on;
**`selectDeFiPositionsV2SectionEnabled`** is the V2 UI gate.
>
> Supporting changes register messengers, persist
**`DeFiPositionsControllerV2`** state, subscribe to
**`DeFiPositionsControllerV2:stateChanged`**, extend
fixtures/logs/tests, and document the flag in the feature-flag registry.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f05ce2e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#32387) ## **Description** This PR integrates a daily anonymizer into the developer usage-metrics collection tooling. The usage-tracking Yarn plugin now opportunistically triggers the anonymizer once per 24h window: the first non-CI `yarn <script>` run after the window opens spawns the anonymizer as a detached, non-blocking child process. The 24h gate is read from `last_run_at` in `~/.tool-usage-collection/anonymizer-state.json`; the anonymizer re-checks and claims the window itself, so concurrent spawns are harmless. The trigger is best-effort and never throws into the Yarn hot path — failures are written to a local `anonymizer.log`. The anonymizer itself is published as [`@metamask/tooling-insight`](https://github.com/MetaMask/experimental-tooling-insight) and consumed here as a `devDependency`, pinned to a specific preview build. The trigger is disabled when `CI` is set or `TOOL_USAGE_COLLECTION_OPT_IN=false`, consistent with the rest of the collection tooling. Also cleans up `scripts/tooling/README.md` (trims the inlined dev-tooling-explorer instructions in favor of linking its repo, documents the daily anonymizer) and removes the now-unused `scripts/tooling/tsconfig.json`. Uses the package [tooling-insight 1.0.0](https://www.npmjs.com/package/@metamask/tooling-insight) ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: [MCWP-644](https://consensyssoftware.atlassian.net/browse/MCWP-644) ## **Manual testing steps** N/A — developer-only tooling, disabled in CI. Verified via unit tests in `.yarn/plugins/plugin-usage-tracking.test.ts`: 1. Run `yarn jest .yarn/plugins/plugin-usage-tracking.test.ts`. 2. Confirm the anonymizer is not spawned when `last_run_at` is within 24h. 3. Confirm the plugin is disabled (no spawn) when `CI` is set. ## **Screenshots/Recordings** N/A — non-user-facing developer tooling change. ### **Before** <!-- [screenshots/recordings] --> ### **After** Example logs from `tail -f ~/.tool-usage-collection/anonymizer.log`: ``` {"ts":"2026-07-30T12:07:25.806Z","cli":"anonymizer","level":"info","message":"push complete","extra":{"status":200}} {"ts":"2026-07-30T12:07:25.810Z","cli":"anonymizer","level":"info","message":"publish complete","extra":{"latestDay":"2026-07-27","sampleCount":410}} ``` Example state `~/.tool-usage-collection/anonymizer-state.json` file: ```json { "version": 1, "instance_uuid": "test", "last_pushed_day": "2026-07-27", "last_run_at": "2026-07-30T12:07:24.665Z" } ``` ## **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** - [ ] 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. [MCWP-644]: https://consensyssoftware.atlassian.net/browse/MCWP-644?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Developer-only Yarn plugin and local opt-out/CI guards; no mobile app runtime changes. Residual risk is limited to optional background anonymizer runs on developer machines when collection is enabled. > > **Overview** > Adds **`@metamask/tooling-insight`** as a dev dependency and wires the usage-tracking Yarn plugin to **spawn `daily-anonymizer.mjs` in the background** after a logged root script finishes, gated by a **24h `last_run_at`** check in `~/.tool-usage-collection/anonymizer-state.json` (skipped in CI/opt-out; errors go to `anonymizer.log` without affecting `yarn`). > > **Yarn event logging is tightened** so only **user-typed root scripts** are recorded: `MM_TOOL_USAGE_PARENT` is set at **module load** (so nested `yarn` children and repeated `factory()` calls are handled correctly), **install lifecycle scripts** are ignored, and **interrupted** runs are detected via a broader set of signal exit codes plus **SIGINT/SIGHUP/SIGTERM listeners** when exit codes are ambiguous. > > Docs in `scripts/tooling/README.md` describe root-only tracking, interrupted events, and the daily anonymizer; **`scripts/tooling/tsconfig.json` is removed**; depcheck ignores the new package. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2e78419. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Signed-off-by: Nicolas MASSART <nicolas.massart@consensys.net>
<!--
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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
Adds Appium smoke coverage for wallet platform flows under
`tests/smoke-appium/wallet/`, continuing the MMQA-2006 migration to
Playwright + Appium.
This PR includes:
- Incoming transactions Appium smoke coverage
- EVM provider events Appium smoke coverage (accountChanged /
chainChanged via native UI)
- mUSD conversion happy path Appium smoke: First Time User and Asset
Overview (classic conversion with Money Hub off)
- Supporting mocks and POM updates (intents relay quote/status hosts,
WalletView mUSD/asset locators, NetworkConnectMultiSelector Appium row
locators, confirmation Done-button wait)
### Appium specs added
| Spec | Cases |
| --- | --- |
| `incoming-transactions.spec.ts` | Incoming transfer visibility /
activity |
| `connections/evm-provider-events.spec.ts` | Permitted dapp notified of
account and chain changes |
| `musd-conversion-happy-path.spec.ts` | First Time User USDC→mUSD;
Asset Overview USDC→mUSD |
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
CHANGELOG entry: null
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/MMQA-2006
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```gherkin
Feature: Wallet Appium smoke (MMQA-2006)
Scenario: run mUSD conversion happy path on Android
Given a main-e2e release APK is available at build/ci-main-e2e/app-prod-release.apk
And Android emulator-5554 is booted
When I run yarn appium-smoke:android --grep "mUSD Conversion Happy Path"
Then First Time User and Asset Overview cases pass
Scenario: run EVM provider chain-permission case on iOS and Android
Given a main-e2e MetaMask.app / APK is installed
When I run yarn appium-smoke:{ios|android} --grep "notifies a permitted dapp of the new chain ID when the network changes"
Then the case passes on both platforms
```
Local validation performed:
- Android (`emulator-5554`): mUSD Conversion Happy Path — 2 passed
- Android + iOS: EVM provider chain ID / network permission case —
passed
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
N/A — E2E/test-only changes; no product UI changes.
### **Before**
N/A
### **After**
N/A
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
- [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**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
## **Description**
Fixes High-severity list recycling in Add Asset token search
(`SearchTokenResults`).
The token-search `FlashList` keyed rows by array index and defined
`renderItem` inline. As search results shift on every keystroke, index
keys force full remounts instead of cheap recycles and can attach
selection state to the wrong row.
This change:
1. Keys rows by stable `${chainId}-${address}` identity
2. Extracts a module-level `React.memo` row component and a
`useCallback` `renderItem`
3. Moves per-row selected/already-added lookups into the row path via a
memoized selected-address set
4. Adds a unit test that selection stays on the correct token when
`searchResults` reorder
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes: #31314
Fixes:
[ASSETS-3722](https://consensyssoftware.atlassian.net/browse/ASSETS-3722)
## **Manual testing steps**
```gherkin
Feature: Add Asset token search selection stability
Scenario: selection stays on the correct token while refining search
Given the user is on Add Asset search with multiple token results
When the user selects a token
And then types or deletes characters so the result list reorders
Then the previously selected token remains selected
And no unrelated row appears selected
```
Unit coverage: `yarn jest
app/components/Views/AddAsset/components/SearchTokenResults/SearchTokenResults.test.tsx
--coverage=false` (7 passed).
Also verified: `npx eslint` on touched files; `yarn lint:tsc`.
## **Screenshots/Recordings**
N/A — performance/recycling fix; no intentional UI change.
### **Before**
N/A
### **After**
N/A
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### 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
## **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.
<div><a
href="https://cursor.com/agents/bc-dbda72c5-a276-4b2f-87ec-b43641e4be31"><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> <a
href="https://cursor.com/automations/ad0b477a-8482-11f1-a7d1-d6b4613131ce"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/view-automation-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/view-automation-light.png"><img
alt="View Automation" width="141" height="28"
src="https://cursor.com/assets/images/view-automation-dark.png"></picture></a> </div>
[ASSETS-3722]:
https://consensyssoftware.atlassian.net/browse/ASSETS-3722?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Scoped UI/list recycling fix in Add Asset search with unit test
coverage; no auth, persistence, or API changes.
>
> **Overview**
> Fixes **incorrect checkbox/selection state** in Add Asset token search
when the result list reorders as the user types.
>
> `SearchTokenResults` now keys `FlashList` rows with
**`${chainId}-${address}`** instead of array index, and uses a memoized
**`SearchTokenResultRow`** with a stable **`useCallback` `renderItem`**
and a **`Set` of selected addresses** so recycled rows bind to the right
token. A unit test asserts selection stays on **Token A** after
**`searchResults`** are reordered.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
158faf8. 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 Agent <cursoragent@cursor.com>
Co-authored-by: prithpal.sooriya <prithpal.sooriya@consensys.net>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
<!--
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?
-->
What's included:
- Provider + container: PerpsProOrderFormPanel now mounts a single
PerpsOrderProvider (initialized from the route market) and a content
component that consumes usePerpsOrderContext - following the same
provider convention as lite (usePerpsProOrderForm): composes the
existing derived value hooks into display ready summary strings and a
typed notices list, owns Pro-only state (reduceOnly, sheet visibility),
and exposes a trimmed, direct path submit handler.
- Submission through usePerpsOrderExecution with the guards from lite's
direct path (validation, slippage-exceeds, cross-margin warning modal,
TP/SL-separate flip handling, toasts, pending config cleanup). Order
type moves into orderForm.type; leverage/slippage bottom sheets, TP/SL
navigation, Add Funds, and Mid-price are wired.
- Scope boundaries: pay-with-any-token/deposit-and-order is out of v1;
bespoke Pro-specific analytics trace names/abandonment are deferred to a
product/analytics sync (existing screen-viewed, place-order-tap, and
controller-owned terminal trade events are wired).
- Shared helpers: the order-sizing math, OrderParams/tracking-data
assembly, and TP/SL warning flags are pure functions (utils/orderSizing,
utils/orderParams, utils/tpslValidation) that both the Pro and lite
forms consume, so the two derive these values from one implementation.
Includes unit tests for the helpers plus branch coverage for the Pro
hook and panel.
1. What is the reason for the change?
The Pro order form was UI-only scaffolding — it couldn't read live order
state or place an order, so the Pro market screen was non-functional for
trading. This ticket (split out from TAT-3580 to review UI and logic
independently) supplies the missing state/logic wiring.
2. What is the improvement/solution?
A working Pro order form that behaves like the lite form (direction,
leverage, order type, limit price, size/slider, reduce-only, TP/SL,
validation banners, summary values, direct submission with toasts) while
staying on the Pro screen on submit. It's built by reusing lite's hooks
and shared pure helpers — minimizing duplication and keeping order form
behavior consistent across both forms.
Extracted the three repeated inline derivations (deriveOrderSizing,
getPerpsOrderTpSlWarnings, buildPerpsOrderTrackingData) that both the
lite and Pro forms were computing independently into shared pure
utilities. Two components calling the same controller functions but each
re-deriving the same values inline is still duplication — just one level
lower than it looks. With these as named, tested functions a bug fix or
edge-case correction (e.g. the TP/SL validationReferencePrice logic) is
a single edit that applies to both forms automatically, rather than a
change that must be made twice. Pure functions are also directly unit
testable with plain inputs, without spinning up hooks, context
providers, or React state - orderSizing.test.ts and
tpslValidation.test.ts cover the math paths that previously could only
be exercised through the full component. The forms now contain only
React wiring; the arithmetic lives in explicitly named, documented,
independently verified utilities..
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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: Wired Pro mode order form to state and hooks for submit
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3595
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```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**
<!-- mms-check: type=screenshot required=true -->
<!-- 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/0883e47a-b0de-4b77-b525-e553e5230e06
https://github.com/user-attachments/assets/144f0616-b1b8-4d0a-802e-d0e49b784190
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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 perps order submission and shared sizing/params assembly
for both lite and Pro; behavior is intended to match lite but
regressions could affect margin, slippage, or post-submit TP/SL
handling.
>
> **Overview**
> **Pro trading on the market screen** replaces the placeholder order
panel with a full flow: `PerpsOrderProvider`, `usePerpsProOrderForm`
(validation, slippage/cross-margin guards, reduce-only, TP/SL, direct
submit with toasts), and bottom sheets wrapped in **Modal** so overlays
aren’t clipped in the Pro layout. `PerpsProMarketView` drops local
order-type state and passes `market` into the panel.
>
> **Shared order-form logic** moves repeated inline math from lite
`PerpsOrderView` into **`deriveOrderSizing`**, **`buildPerpsOrderParams`
/ `buildPerpsOrderTrackingData`**, and **`getPerpsOrderTpSlWarnings`**,
with unit tests. Lite and Pro both consume these; digit caps use
**`MAX_PERPS_INPUT_DIGITS`** across close/limit/order inputs.
>
> **Submission correctness:** `usePerpsOrderExecution.placeOrder` now
**returns `OrderResult`**, and lite/Pro **skip TP/SL follow-up and
`clearPendingTradeConfiguration` when the order fails**.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e964004. 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: abretonc7s <107169956+abretonc7s@users.noreply.github.com>
…34037) ## **Description** Memoizes `selectTokensByAddress` so consumers do not re-render when the address→token map content is unchanged. `selectTokensByAddress` previously used plain `createSelector` and always built a fresh map via `reduce`. When `selectTokens` recomputed with equal content under a new array reference (e.g. unrelated `allTokens` mutations), every address-map subscriber re-rendered. This change: 1. Switches `selectTokensByAddress` to `createDeepEqualSelector` (same pattern as `selectAllTokensFlat` / sibling token selectors) 2. Returns a module-level frozen empty object (`EMPTY_TOKENS_BY_ADDRESS`) for the empty case 3. Adds referential-stability unit tests (same state, equal-content states, and empty object) ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #31357 Fixes: [ASSETS-3726](https://consensyssoftware.atlassian.net/browse/ASSETS-3726) ## **Manual testing steps** ```gherkin Feature: token-by-address selector stability Scenario: address-keyed token lookups stay stable on unrelated store updates Given a wallet with tokens on the selected network When an unrelated Redux update occurs that does not change selected-account token contents Then components subscribed via selectTokensByAddress should keep a stable selector result reference ``` Unit coverage: `yarn jest app/selectors/tokensController --coverage=false` (26 passed), including referential-stability assertions. Also verified with `npx eslint` on touched files and `yarn lint:tsc`. ## **Screenshots/Recordings** N/A — selector-only performance fix; no UI changes. ### **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 ## **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. <div><a href="https://cursor.com/agents/bc-7ca6a05b-5a70-4126-b484-a9f59697ad70"><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> <a href="https://cursor.com/automations/ad0b477a-8482-11f1-a7d1-d6b4613131ce"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/view-automation-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/view-automation-light.png"><img alt="View Automation" width="141" height="28" src="https://cursor.com/assets/images/view-automation-dark.png"></picture></a> </div> [ASSETS-3726]: https://consensyssoftware.atlassian.net/browse/ASSETS-3726?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Selector-only memoization change with unit tests; no auth, persistence, or UI behavior changes. > > **Overview** > **`selectTokensByAddress`** now keeps a stable result reference when the selected-account token list is unchanged, so Redux subscribers avoid extra re-renders after unrelated token-store updates. > > The selector uses **`createDeepEqualSelector`** (aligned with other token selectors) instead of **`createSelector`**, which always produced a new address→token map via **`reduce`**. Empty token lists return a shared frozen **`EMPTY_TOKENS_BY_ADDRESS`** instead of a new `{}` each time. Tests assert referential stability for repeated calls, equal-content state, and the empty case. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e35f28b. 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 Agent <cursoragent@cursor.com> Co-authored-by: prithpal.sooriya <prithpal.sooriya@consensys.net> Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
mirroing MetaMask/metamask-extension#44979
This PR wires Stellar-specific asset state into the mobile UI by
introducing selectors and hooks for (1) trustline
activation/deactivation and (2) native spendable-balance breakdown, then
surfaces these in the token list and token details screens.
**Changes:**
- Added Stellar selectors to detect activation requirements and compute
spendable/reserved balances from AssetsController metadata.
- Implemented Snap client request wrappers for Stellar trustline
add/delete, plus UI hooks/components to activate/deactivate assets.
- Updated token list/details UI to show an “Inactive” badge, an
activation CTA card, a “Deactivate asset” menu item, and a
spendable-balance section.
<!-- mms-check: type=text required=true -->
<!--
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**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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 Stellar Asset Component and wire them into
asset component (asset details, asset row and asset button)
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes:
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```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**
<!-- mms-check: type=screenshot required=true -->
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
All evm / non evm token should stay the same behaviour
https://www.loom.com/share/fd4f939c57a0467d9c9fd0c3488f88fe
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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**
> Trustline add/delete flows submit on-chain changes via the Stellar
Snap and affect wallet asset visibility and balances, though behavior is
gated to Stellar classic/native assets with tests covering main paths.
>
> **Overview**
> Adds **Stellar-specific asset UX** on top of new Redux selectors and
Snap trustline requests.
>
> **Selectors & Snap:** New `stellar-assets` selectors read
`AssetsController` metadata for trustline limits, activation need, and
native XLM spendable vs reserved amounts. `changeTrustOpt` add/delete
wrappers call the Stellar wallet Snap. Asset list filtering keeps
activation-capable classic assets visible even at zero balance.
>
> **UI:** Token rows show an **Inactive** badge when activation is
required. Token details show an **Activate** card (success →
transactions view; errors via notification) and, for native XLM,
**SpendableBalanceSection** instead of the default balance block when
spendable data exists. **More** menu adds **Deactivate asset** when the
trustline is active, with non-zero balance blocked before Snap delete.
>
> **Hooks:** `useAssetActivation` and `useSpendableBalance` connect
components to selectors and Snap; i18n strings added for activation,
spendable balance, and deactivate copy.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
58ad51a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…4073) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Removes the `socialAiTSA905AbtestQuickBuyKeyboard` A/B test and its associated logic so Quick Buy always shows the numeric keyboard (and no longer offers the percentage slider). **Why:** For **8.6.0** we want the keyboard as the only Quick Buy amount input, so this A/B test is no longer needed. **Why remove the flag (not just default to treatment):** On **8.5.0**, a small bug means we need to keep the flag at **100% control**. If we left the flag in place for 8.6.0, we would need **100% treatment** on 8.6.0 while keeping **100% control** on 8.5.0 — an unnecessarily complex LaunchDarkly split across versions. Removing the flag in 8.6.0 avoids that conflict. This PR: - Deletes the keyboard vs slider A/B config and analytics registry mapping - Removes `useKeyboard` branching across Quick Buy Root/Context/footer/keypad/amount/skeleton - Deletes `QuickBuyPercentageSlider` and the control (slider) UI path - Leaves the separate TSA-612 Asset Details Quick Buy A/B test untouched ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Updated Quick Buy to always use the numeric keyboard for amount entry ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: TSA-963 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Quick Buy keyboard-only amount entry Scenario: user opens Quick Buy and enters an amount with the keypad Given the user opens Quick Buy for a supported token When the sheet finishes opening Then the numeric keypad is shown by default And the percentage slider is not shown When the user types an amount on the keypad And taps Buy Then the trade flow proceeds with the entered amount Scenario: user selects a quick-amount pill Given Quick Buy is open with the keypad visible When the user taps a buy quick-amount pill Then the amount updates to that preset And the keypad is dismissed When the user taps the amount headline Then the keypad reopens ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — removes an A/B branch; keyboard UI is unchanged from the treatment path already shipping. ### **Before** <!-- [screenshots/recordings] --> Control: percentage slider. Treatment: numeric keypad. ### **After** <!-- [screenshots/recordings] --> Numeric keypad only (former treatment). ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [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.
## **Description**
<!-- mms-check: type=text required=true -->
Crypto-native users encountering HIP-3 tickers (e.g. NVDA, CL) often
lack context on the underlying asset, which creates a trust barrier on
stock and commodity markets. This PR adds an **About** section on the
Perps Lite market detail screen (`PerpsMarketDetailsView`) that shows
the Hyperliquid/Terminal asset description when available.
**What changed**
- New `PerpsMarketAboutSection` with title `About {assetName}`, a 3-line
collapsed description, and a bold underlined **Read more** control that
expands the full text then disappears
- Graceful fallback: the section is not rendered when the market has no
description (no empty state)
- New `usePerpsMarketAboutTracking` hook that fires
`PERPS_UI_INTERACTION` with:
- `market_about_section_displayed` when the section is rendered
(includes `market_symbol`, `market_type`, `description_length`)
- `market_about_section_viewed` once per screen session when the section
scrolls into view
- Wired into `postMarketInsightsSections` on the Lite market details
screen; description is sourced from existing
`PerpsMarketData.description` (no extra network fetch)
- Unit tests for the component, tracking hook, and view integration
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
CHANGELOG entry: Added an About section on Perps market details so users
can read a short description of the underlying asset
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Refs: TAT-3162
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```gherkin
Feature: Perps market About section (Lite mode)
Scenario: user sees About when a market has a description
Given the user is in Perps Lite mode
And a market has a non-empty Hyperliquid/Terminal description
When the user opens that market's detail screen
Then an "About {asset name}" section is shown
And the description is clamped to 3 lines when longer than 3 lines
And a bold underlined "Read more" control is shown when truncated
Scenario: user expands the About description
Given the About section is shown with "Read more" visible
When the user taps "Read more"
Then the full description is shown
And the "Read more" control disappears
Scenario: user does not see About when description is missing
Given a market has no description from Hyperliquid/Terminal
When the user opens that market's detail screen
Then the About section is not rendered
And there is no empty state or placeholder text
Scenario: analytics fire for display and view
Given a market with a description
When the user opens the market detail screen
Then PERPS_UI_INTERACTION with interaction_type market_about_section_displayed fires once
When the user scrolls the About section into the viewport
Then PERPS_UI_INTERACTION with interaction_type market_about_section_viewed fires at most once per screen session
```
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
N/A — UI screenshots not attached in this PR; please add device captures
of collapsed (3-line + Read more) and expanded About states before Ready
for review.
### **Before**
N/A — no About section on market details
### **After**
<img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 -
2026-07-29 at 16 03 07"
src="https://github.com/user-attachments/assets/5ae2fde9-3cf5-4517-8c84-1748a13ad65b"
/>
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
- [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**
> UI-only Perps market details change with analytics and tests; no auth,
payments, or new network layer.
>
> **Overview**
> Adds an **About** block on Perps Lite market details when
`PerpsMarketData.description` is present, using existing market metadata
(no new fetch).
>
> **`PerpsMarketAboutSection`** shows “About {assetName}”, clamps copy
to three lines, and offers **Read more** (hidden measure text decides
truncation). Missing or whitespace-only descriptions render nothing.
>
> **`usePerpsMarketAboutTracking`** gates visibility, wires `onLayout` /
`ScrollView` `onScroll`, and emits `market_about_section_displayed` and
a once-per-market `market_about_section_viewed` `PERPS_UI_INTERACTION`
with symbol, type, and description length. The section is the first
entry in `postMarketInsightsSections`; new i18n strings and test IDs
support UI and integration tests.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
144e586. 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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
This PR integrates V1 of the new money utils package. This shouldn't
lead to any behaviour changes.
<!-- mms-check: type=text required=true -->
<!--
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**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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: integrate money utils v1
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: MUSD-1238
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```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**
<!-- mms-check: type=screenshot required=true -->
<!-- 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**
<!-- mms-check: type=checklist required=true -->
<!--
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**
> Refactor-only dependency extraction with stable re-exports; intended
to be behavior-neutral, though confirmation flows that use
`MUSD_TOKEN.image` now depend on the package providing that field.
>
> **Overview**
> Adds **`@metamask/money-account-utils` ^1.0.0** and moves shared mUSD
token metadata, chain maps, asset IDs, and helpers (`isMusdToken`,
`getTokenDisplaySymbol`, money-account chain guards, etc.) out of
**`app/components/UI/Earn/constants/musd.ts`**, re-exporting them so
existing imports from that path stay stable.
>
> **`MUSD_TOKEN`** is now built by spreading the package’s `MUSD_TOKEN`
and attaching the mobile-only **`imageSource`** (local PNG).
Earn-specific config (buyable chains, APY, geo fallback, toast delay)
remains in the local file. Inline definitions of addresses, decimals,
and guard functions are removed from mobile in favor of the shared
package.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2580bee. 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: Claude Opus 5 (1M context) <noreply@anthropic.com>
## **Description** When a MetaMask Pay quote fails (e.g. insufficient balance), the confirmation screen previously showed a generic "No quotes available" message with no explanation. Core now returns a structured error on each quote; this PR surfaces it. - Adds a `NoQuoteAlert` component that shows a reason-appropriate collapsed message (`insufficient-source-balance` → "Insufficient balance"; anything else → the generic string), with a double-tap to toggle a full error view showing core's `message` and `detail` rows. - Wires the alert into the existing "Pay with" row alert system via a new selector, hook, and `content` prop on `AlertMessage` — the alert blocks confirm while prices/fees continue to render. - Fixes the post-quote "no quotes" check so same-token withdraw flows (e.g. MUSD → MUSD) correctly surface an alert when their amounts list is empty. Depends on MetaMask/core#9143, which adds the structured `quoteError` (`message`, `detail`, `reason`) to each pay quote. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9143 ## **Manual testing steps** ```gherkin Feature: Pay quote error surfaced in confirmation alert Scenario: a pay quote can't be used Given a transaction that requires a MetaMask Pay quote And the selected pay token produces a quote that can't be used When the confirmation screen loads Then a blocking alert is shown on the "Pay with" row And the confirm button is disabled Scenario: tapping the alert reveals the full error When the user double-taps the alert Then the full error message and detail rows are shown And double-tapping again collapses it ``` ## **Screenshots/Recordings** N/A ### **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 - [ ] 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** > Changes confirmation blocking behavior and pay quote error UX on the critical confirm path, but scope is UI wiring and read-only controller state with tests. > > **Overview** > When MetaMask Pay returns a quote that fails validation (e.g. insufficient balance), confirmations now show a **blocking “Pay with” alert** with a reason-specific collapsed message instead of only the generic “no quotes” copy. Core’s structured `quoteError` is read per transaction via a new selector and `useTransactionPayQuoteError`, and `useNoPayTokenQuotesAlert` treats a present `quoteError` as a separate show condition while still blocking confirm. > > A new **`NoQuoteAlert`** component renders that error: double-tap toggles between the collapsed string (`insufficient-source-balance` uses the insufficient-balance i18n key) and the full `message` plus optional `detail` rows. **`AlertMessage`** gains an optional `content` slot (with layout split into `content` vs `message` styles), and the custom-amount flow passes **`alertContent`** from the alert system so rich UI can replace plain banner text. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a9c3eb4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…ty helpers (#34111) ## **Description** Refactors `getMetaMaskPayProperties` in `app/core/Engine/controllers/transaction-controller/metrics_properties/metamask-pay.ts` for readability. The builder had grown into a hard-to-follow mix of inline branching and a ~130-line `addPayTypeProperties` doing six unrelated jobs. Changes: - **Root function is now a flat, annotated composition** — each helper call is preceded by the exact `mm_pay_*` properties it sets, so the full metric surface is visible at a glance. - **Split into small, single-purpose helpers**: `addPolymarketAccountCreated`, `addBaselinePayProperties`, `addAmountProperties`, `addQuoteProperties`, `addFiatPaymentProperties`, `addChildTransactionStep`, `addDustProperties`, `addTimeToComplete`, plus shared `getTransactionPayData` / `isSwapOrBridge` / `isPayTransaction` utilities. - **Maximum metrics when `TransactionPayController.transactionData` is unavailable**: since `transactionData` is not persisted but `transactionMeta.metamaskPay` is, the old startup-error-only backfill (`addFailedOnStartupMetrics`) is generalized into `addPersistedPayMetadata`. Whenever `transactionData` is missing, we now backfill `mm_pay_receiving_value_usd`, `mm_pay_provider_fee_usd`, `mm_pay_network_fee_usd`, `mm_pay_strategy` and `mm_pay_fiat_provider` from the persisted metadata, regardless of the error message. - **Payment method recovered from the persisted Ramps order**: for fiat-funded transactions in the backfill path, `mm_pay_payment_method_selected` is resolved by joining `metamaskPay.fiat.orderId` against `RampsController.orders` (which IS persisted, `providerOrderId` match — same join `useFiatPaymentMethodName` uses) and normalizing `order.paymentMethod.id` via the existing `normalizeMetaMaskPayPaymentMethod`. Falls back to a generic `'fiat'` value when the order isn't in cache or has no payment method, so fiat-funded transactions are never misreported as `'crypto'`. - **`mm_pay_strategy` defaults to `'relay'` for non-fiat backfills**: the non-fiat strategy isn't persisted on `metamaskPay` yet, so the backfill path temporarily assumes the most common strategy. To be replaced once strategy info is persisted in TransactionController's `metamaskPay` state. - Removed redundant conditions (`isPayType || properties.mm_pay`) and the mutate-order coupling between helpers; `txPayData` is resolved once at the top and threaded through. No behavior change other than the intentional backfill changes above. Tests updated/extended accordingly (68 passing). ### Property coverage matrix Scope: pay transaction, parent/standalone path (`isPayTransaction` true). <details> <summary><b>When <code>txPayData</code> EXISTS (live controller state)</b></summary> **Guaranteed (unconditional):** | Property | Source | |---|---| | `mm_pay` | `true`, always | | `mm_pay_payment_method_selected` | `'crypto'` default, overridden by fiat method if `fiatPayment.selectedPaymentMethodId` | | `mm_pay_quote_skipped` | quote count comparison, always a boolean | | `mm_pay_transaction_step_total` | `quotes.length + 1`, always | | `mm_pay_transaction_step` | `= step_total`, always | **Conditional:** | Property | Set when | |---|---| | `mm_pay_chain_selected` | `metamaskPay.chainId` exists | | `mm_pay_token_selected` | `paymentToken.symbol` or TokensController lookup succeeds | | `mm_pay_use_case` | type matches `USE_CASE_MAP` | | `mm_pay_sending_value_usd` | a token with `skipIfBalance: false` exists | | `mm_pay_receiving_value_usd` | `totals` exists | | `mm_pay_metamask_fee_usd` | `totals` exists | | `mm_pay_provider_fee_usd` | `totals` exists | | `mm_pay_network_fee_usd` | `totals` exists | | `mm_pay_strategy` | first real quote strategy is Relay or Fiat | | `mm_pay_fiat_provider` | fiat method selected + rampsQuote provider parses | | `mm_pay_fiat_token_target` | fiat method selected + `cryptoTranslation.symbol` | | `mm_pay_fiat_chain_target` | fiat method selected + chain resolvable from caipAssetId/rampsQuote | | `mm_pay_time_to_complete_s` | event is FINALIZED + a numeric `submittedTime` | | `polymarket_account_created` | type is predictDeposit / predictDepositAndOrder (always set for those, path-independent) | </details> <details> <summary><b>When <code>txPayData</code> is MISSING (restart / failure / state cleared)</b></summary> **Guaranteed (unconditional):** | Property | Value | |---|---| | `mm_pay` | `true` | | `mm_pay_payment_method_selected` | fiat: normalized method from the persisted Ramps order (`orderId` → `providerOrderId` join), or generic `'fiat'` on cache miss; `'crypto'` for non-fiat | | `mm_pay_strategy` | `'fiat'` if `metamaskPay.fiat` exists, else `'relay'` (temporary assumption until strategy is persisted; requires `metamaskPay` to exist) | **Conditional (backfilled from persisted `transactionMeta.metamaskPay` / `RampsController.orders`):** | Property | Set when | |---|---| | `mm_pay_chain_selected` | `metamaskPay.chainId` persisted | | `mm_pay_token_selected` | TokensController lookup succeeds (no `paymentToken` fallback) | | `mm_pay_use_case` | type matches `USE_CASE_MAP` | | `mm_pay_receiving_value_usd` | `metamaskPay.targetFiat` persisted | | `mm_pay_provider_fee_usd` | `metamaskPay.bridgeFeeFiat` persisted | | `mm_pay_network_fee_usd` | `metamaskPay.networkFeeFiat` persisted | | `mm_pay_fiat_provider` | `metamaskPay.fiat.provider` parses | | `mm_pay_time_to_complete_s` | same conditions as live path (does not need `txPayData`) | | `polymarket_account_created` | same conditions as live path | **NEVER set (not recoverable — not persisted anywhere):** - `mm_pay_sending_value_usd` - `mm_pay_metamask_fee_usd` - `mm_pay_quote_skipped` - `mm_pay_transaction_step` / `mm_pay_transaction_step_total` - `mm_pay_fiat_token_target` / `mm_pay_fiat_chain_target` </details> The child-transaction path additionally always sets `mm_pay_transaction_step`, and conditionally `mm_pay_payment_method_available` / `mm_pay_payment_method_presented` (from parent UI metrics) and `mm_pay_dust_usd` (requires `txPayData` quotes). **Known caveats (backfill path):** - `mm_pay_payment_method_selected` falls back to a generic `'fiat'` for fiat transactions when the Ramps order isn't in the persisted `RampsController.orders` cache (e.g. order created on another device, wiped storage) or the order has no `paymentMethod` — the specific method (card vs bank transfer) can't be recovered in that case. - `mm_pay_strategy: 'relay'` for non-fiat backfills is an assumption, not a measurement — non-fiat strategies (relay/across/server) aren't persisted on `metamaskPay`. Follow-up: persist the strategy in TransactionController's `metamaskPay` state and replace the assumption. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1747 ## **Manual testing steps** N/A — covered by unit tests (`yarn jest app/core/Engine/controllers/transaction-controller/metrics_properties/metamask-pay.test.ts`, 68 passing). No user-facing behavior change. ## **Screenshots/Recordings** N/A — no UI changes. ### **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 - [ ] 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] > **Medium Risk** > Intentional analytics semantics change when in-memory pay state is lost; non-fiat strategy and missing Ramps orders use assumed defaults (`relay`, `fiat`) that can mislabel events until strategy is persisted. > > **Overview** > **Refactors** `getMetaMaskPayProperties` into a flat pipeline of single-purpose helpers (`addBaselinePayProperties`, `addAmountProperties`, `addQuoteProperties`, `addFiatPaymentProperties`, `addPersistedPayMetadata`, etc.) so which `mm_pay_*` keys each path sets is visible at the top level. > > **Behavior change when `TransactionPayController.transactionData` is missing** (e.g. restart mid-flow): the old startup-error-only backfill is replaced by **`addPersistedPayMetadata`**, which always runs in that case. It fills receiving value and fee fields from persisted `transactionMeta.metamaskPay`, sets **`mm_pay_strategy`** to `fiat` when `metamaskPay.fiat` exists or **`relay`** otherwise, parses **`mm_pay_fiat_provider`**, and resolves **`mm_pay_payment_method_selected`** by matching `fiat.orderId` to persisted **`RampsController.orders`** (normalized payment method, else `'fiat'` for fiat flows; crypto flows keep `'crypto'`). Live controller state still wins when `transactionData` exists. > > **`mm_pay_time_to_complete_s`** is only computed when `mm_pay` is already set on the finalized parent path. Unit tests are expanded for the new backfill matrix (including `mm_pay_strategy: 'relay'` when only `chainId` is on `metamaskPay`). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 210b376. 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: Sisyphus <clio-agent@sisyphuslabs.ai>
…stry with production default rpc (#34068) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Registers the existing `moneyAccountBalanceSource` remote feature flag in the app-level feature flag registry so production-accurate E2E mocks include its default `rpc` routing policy and the feature-flag pipeline recognizes it. This registry entry does not enable or participate in runtime segmentation. LaunchDarkly targeting already resolves the flag into `RemoteFeatureFlagController`, and `MoneyAccountBalanceService` consumes that value directly. The accompanying messenger test verifies that remote feature-flag state changes reach the service. This change therefore restores E2E and pipeline parity with the existing production flag without changing runtime routing behavior. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Refs: No linked issue — this is app-level registration for an existing production remote feature flag. ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> N/A — there is no user-facing behavior to exercise manually. The registry value and existing Engine messenger wiring are covered by focused unit tests. ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — there are no user-interface changes. ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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`. --> - [ ] 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** > Registry and test-only changes; no production logic or UI behavior is modified in the diff. > > **Overview** > Registers the **`moneyAccountBalanceSource`** remote feature flag in the app feature-flag registry with production default **`rpc`**, so E2E mocks and production-default flag maps match what **`MoneyAccountBalanceService`** already reads at runtime. > > A registry unit test asserts **`moneyAccountBalanceSource`** resolves to **`rpc`** in **`getProductionRemoteFlagDefaults`**. The money account balance service messenger test now also expects **`RemoteFeatureFlagController:stateChange`** to be delegated alongside the existing balance-related actions, documenting that LaunchDarkly updates can refresh balance routing without new wiring in this PR. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 328e1e8. 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**
<!-- mms-check: type=text required=true -->
Polishes the Perps Pro market trading area so form/order-book spacing
matches the Positions panel, and surfaces available balance under the
size input.
1. **Reason:** Screen-edge padding on the Pro form/book row drifted from
the Positions section (`px-2`), and traders lacked a clear
available-balance affordance next to size.
2. **Solution:** Use an 8px (`px-2`) horizontal inset on the trading
layout; move form/book styles into dedicated `.styles.ts` files with the
separator on the form panel; show a tappable `$XX available` row that
opens add funds.
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
CHANGELOG entry: Improved Perps Pro order form layout alignment and
added available balance under size
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes:
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```gherkin
Feature: Perps Pro trading layout and available balance
Scenario: trading area aligns with Positions panel
Given the user opens a Perps market in Pro mode with the order book visible
Then the left edge of the order form aligns with the Positions tab content
And the right edge of the order book aligns with Positions content
And a vertical separator appears between the form and order book with 16px inset on each side
Scenario: available balance opens add funds
Given the user is on the Pro order form with an initialized Perps balance
When the user taps the available balance row under Size
Then the add-funds / deposit flow opens
```
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
### **Before**
N/A
### **After**
N/A
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
- [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**
- [ ] 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-only layout and presentation changes in Perps Pro with tests
updated; add-funds still uses existing callbacks, not new payment logic.
>
> **Overview**
> Aligns the **Perps Pro** trading row with the Positions panel and
improves the order form UX.
>
> **Layout:** `PerpsProMarketLayout` drops the fixed 682px min height
and the standalone vertical divider column in favor of content-driven
heights, **8px** horizontal inset (`PRO_SCREEN_HORIZONTAL_INSET`), and
styles moved into `PerpsProMarketLayout.styles.ts`. The form/book
separator is now a **right border + 16px padding** on
`PerpsProOrderFormPanel` when the order book is visible
(`PerpsProOrderFormPanel.styles.ts`). The order book panel uses full
width instead of `flex-1`.
>
> **Order form:** Available balance under size is a single tappable row
(`AvailableBalanceRow`) that opens add funds, with screen-reader
label/hint. Slippage uses a dedicated pressable with edit icon. Summary
`KeyValueRow`s override default horizontal padding so
margin/liquidation/slippage/fees line up with the form.
`usePerpsProOrderForm` formats balance as localized `"{{amount}}
available"` and shows `"-- available"` until Perps is initialized.
>
> **i18n:** New `perps.pro_order_form.available_balance` /
`available_balance_unavailable` across locale files.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
be0fa55. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
## **Description** Migrates the Background Events Snap Detox smoke suite to Appium. - Moves `tests/smoke/snaps/test-snap-background-events.spec.ts` → `tests/smoke-appium/snaps/` then converts to Appium with `withSnapsFixtures`. - Serial suite: first test uses `restartDevice: true` + `loginAndOpenTestSnaps()` + install; later tests use `restartDevice: false` (no re-login). - Extends `withSnapsFixtures` to pass through `restartDevice` (and related fixture options) and updates Android Test Snaps native helper to re-export scroll labels from selectors. - Skips Android CI cold-boot stabilize when the emulator is already booted, so session reuse is not disrupted and between-test session setup is faster. Split from the bulk approach in #34056 / #33898. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: #34056 Refs: #33898 ## **Manual testing steps** N/A — E2E-only migration; coverage is Appium `SmokeSnaps` CI. Suggested local check: ```bash ANDROID_APK_PATH=build/ci-main-e2e/app-prod-release.apk yarn appium-smoke:android tests/smoke-appium/snaps/test-snap-background-events.spec.ts ``` ## **Screenshots/Recordings** N/A — test-only change; no user-facing UI. ### **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** > Test-only changes to E2E harness and emulator boot logic; no production app behavior. > > **Overview** > Moves **Background Events Snap** coverage from Detox (`tests/smoke/snaps/`) to Appium (`tests/smoke-appium/snaps/`), using a **serial** suite where only the first case restarts the device, logs in, and installs the snap; later cases set `restartDevice: false` and reuse the same session and Test Snaps page. > > **`withSnapsFixtures`** now forwards `restartDevice`, mocks, analytics, and local-node options, and adds **`parseTestSnapStringResult` / `readTestSnapStringResult`** so cancel/list flows work when Android returns bare strings instead of JSON-quoted text. Android scroll label maps are **re-exported from** `TestSnaps.selectors` instead of living in the native helper. > > Timing is tuned for slower Appium/Android (e.g. 30s date offset, 10s duration, polling timeouts). **`EmulatorHelpers`** skips cold-boot stabilize when `sys.boot_completed` is already `1`, so reused emulator sessions are not reset between serial tests. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1f5d402. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Three inline component definitions inside `custom-amount-info.tsx` have been extracted to their own directories under `app/components/Views/confirmations/components/custom-amount/`: - `BuySection` → `CustomAmountBuy` - `Quote` (+ private `PaymentDetailsSkeleton` helper) → `CustomAmountTotals` - `ConfirmButton` (+ private `useButtonLabel` hook) → `CustomAmountConfirmButton` Each extracted component retains identical props and behaviour. The parent file is updated to import and use the new components. This improves discoverability, makes each component independently testable, and reduces the size of the `custom-amount-info.tsx` file by ~200 lines. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** N/A — pure refactor, no behavioural change. Existing unit tests cover the extraction. ## **Screenshots/Recordings** ### **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 ## **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** > Structural refactor with preserved props and logic; confirmation/pay UX paths are unchanged aside from file layout. > > **Overview** > **Refactors** the custom amount confirmation UI by moving three inline blocks out of `custom-amount-info.tsx` into `components/custom-amount/`, without changing behavior. > > `BuySection` becomes **`CustomAmountBuy`** (ramp buy CTA and perps/predict copy). **`Quote`** and its skeleton helper become **`CustomAmountTotals`** (bridge fee/time rows, total vs receive by stage). **`ConfirmButton`** and **`useButtonLabel`** become **`CustomAmountConfirmButton`**. > > The parent only swaps imports and JSX (`<CustomAmountBuy />`, `<CustomAmountTotals />`, `<CustomAmountConfirmButton />`) and drops ~200 lines of local helpers. **Dedicated unit tests** cover the extracted pieces; overlapping cases were removed from `custom-amount-info.test.tsx`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit de510e4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…34107) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Sentry reports \`getBearerToken - unable to proceed, wallet is locked\` from the Top Traders path. The stack is: \`\`\` queryFn (SocialService:fetchLeaderboard) → SocialService.#getAuthHeaders → AuthenticationController.getBearerToken → assertIsUnlocked // throws while locked \`\`\` A previous fix gated \`useTopTraders\` with \`enabled: isUnlocked\`, but AppState foreground still raced ahead of the React commit: \`ReactQueryService\` marks TanStack focused while \`enabled\` could still be true, and \`react-data-query\` uses \`staleTime: 0\`, so \`queryFn\` ran while locked. This PR keeps the unlock \`enabled\` gate and disables \`refetchOnWindowFocus\` / \`refetchOnReconnect\` on that query so automatic refetches cannot hit \`getBearerToken\` during the race. When the wallet unlocks, \`enabled\` flips true and TanStack fetches again (Homepage and deeplinked leaderboard both use this hook). ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- 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** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/TSA-962 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> \`\`\`gherkin Feature: Top traders fetch while locked Scenario: leaderboard does not fetch while locked Given the social leaderboard feature flag is enabled And the wallet auto-locks on background When the app is backgrounded and then foregrounded while still locked Then no SocialService fetchLeaderboard / getBearerToken locked error is thrown Scenario: leaderboard loads after unlock Given the social leaderboard feature flag is enabled And the wallet is locked When the user unlocks the wallet Then useTopTraders enables and loads leaderboard data on Homepage / Top Traders \`\`\` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- 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** <!-- mms-check: type=checklist required=true --> <!-- 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\`. --> - [ ] 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** > Narrow change to one social leaderboard query’s TanStack options; no auth logic changes, with tests updated for the new flags. > > **Overview** > Fixes **Sentry errors** when the Top Traders leaderboard runs `fetchLeaderboard` while the wallet is locked (`getBearerToken` throws). > > `useTopTraders` already gates the query with `enabled: isUnlocked`, but **AppState foreground** can still trigger a TanStack refetch before React applies `enabled: false` after auto-lock (`staleTime: 0` + focus manager). This PR sets **`refetchOnWindowFocus: false`** and **`refetchOnReconnect: false`** on that query so automatic refetches cannot hit auth while locked; unlock still re-enables the query and loads data. > > Unit tests now assert those refetch flags alongside the existing `enabled` behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0709d67. 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>
…tes (#34104) ## **Description** Restores Detox-style serial session reuse for Appium Snap suites that were collapsed into a single Playwright test during the initial migration. **Merge order:** stacked on [#34101](#34101). After #34101 merges to `main`, retarget this PR’s base to `main` and merge. - Splits `wasm`, `installed`, and `client-status` back into connect/setup + follow-up cases. - Serial suites: first test uses `restartDevice: true` + `loginAndOpenTestSnaps()` + install; later tests use `restartDevice: false` (no re-login). - Background Events cancel: call `blurActiveWebViewInput()` after filling the cancel event id so the Android keyboard does not block later Test Snaps controls (`cancel` / `getBackgroundEvents`). ## **Changelog** CHANGELOG entry: null ## **Related issues** Depends on: #34101 Refs: #34101 ## **Manual testing steps** N/A — E2E-only change; coverage is Appium `SmokeSnaps` CI. Suggested local check: ```bash ANDROID_APK_PATH=build/ci-main-e2e/app-prod-release.apk yarn appium-smoke:android tests/smoke-appium/snaps/test-snap-wasm.spec.ts ANDROID_APK_PATH=build/ci-main-e2e/app-prod-release.apk yarn appium-smoke:android tests/smoke-appium/snaps/test-snap-installed.spec.ts ANDROID_APK_PATH=build/ci-main-e2e/app-prod-release.apk yarn appium-smoke:android tests/smoke-appium/snaps/test-snap-client-status.spec.ts ANDROID_APK_PATH=build/ci-main-e2e/app-prod-release.apk yarn appium-smoke:android tests/smoke-appium/snaps/test-snap-background-events.spec.ts ``` ## **Screenshots/Recordings** N/A — test-only change; no user-facing UI. ### **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** > E2E test-only changes with no production code paths affected. > > **Overview** > Restores **Detox-style serial Appium runs** for WASM, installed, and client-status Snap smoke specs by splitting each former single test into a **connect/setup** case and one or more **follow-up** cases under `describe.configure({ mode: 'serial', timeout: 150_000 })`. > > The first test in each suite uses `withSnapsFixtures(..., { restartDevice: true })` with `loginAndOpenTestSnaps()` and snap install; later tests use `restartDevice: false` and only exercise snap UI (no re-login). Background Events **schedule/list/cancel** now calls `TestSnaps.blurActiveWebViewInput()` after filling the cancel event id so the Android keyboard does not block cancel/get-results taps. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f07f321. 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** When paying gas with a token on Linea (e.g. mUSD), the network fee token dropdown could show a blank or letter fallback icon if TokensController had not hydrated `token.image` yet. This change updates `GasFeeTokenIcon` to fall back to the CDN URL from `getAssetImageUrl`, matching confirmation `TokenIcon`. It also uses the token symbol for the avatar name when the image is missing. ## **Changelog** CHANGELOG entry: Fixed missing token logos in the network fee token dropdown when paying with a token such as mUSD ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1739 ## **Manual testing steps** ~~~gherkin Feature: gas fee token icons Scenario: user sees mUSD logo in network fee token dropdown on Linea Given the user has mUSD on Linea And the user starts a send of any token on Linea When the user opens the network fee token dropdown Then mUSD shows its logo ~~~ ## **Screenshots/Recordings** N/A — pending manual verification on device/simulator. ### **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 - [ ] 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.
…QA-2147) (#34049) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** <!-- mms-check: type=text required=true --> Performance Android BrowserStack dual builds (`main-e2e-bs-with-srp` / `main-e2e-bs-without-srp`) previously always compiled fresh on app-change PRs, even when native sources matched a prior run's `@expo/fingerprint`. This aligns the performance dual-upload path with Detox E2E (`build-android-e2e.yml`): 1. Look up prior `android-apk-main-e2e-bs-*` artifacts by fingerprint (`find-reusable-build` across `ci.yml` / `run-performance-e2e*.yml`). 2. **Miss** → fresh dual Gradle builds (unchanged). 3. **Hit + test-only** (`main_branch_only` / `reuse_main_builds`) → re-upload APKs as-is (app JS unchanged; same as E2E's no-repack path). 4. **Hit + app changes** → skip Gradle, `@expo/repack-app` both profiles with current JS (env from `builds.yml`, including `IS_PERFORMANCE_TEST` / SRP bake-ins), then upload to BrowserStack. `scripts/repack.js` gains optional path overrides (`REPACK_SOURCE_APK`, etc.) so both profiles can be repacked without colliding. Does **not** reuse Appium smoke `main-e2e` APKs (different ABI/flags/profile). Test-only PRs still prefer BrowserStack `custom_id` reuse on main when available; fingerprint is the fallback / app-change savings path. ### Measured CI savings (this PR) Same native fingerprint (`bf403cf3…`) on consecutive runs of this PR: | Path | Run | Dual Android BS path (resolve → upload done) | Gradle dual builds | Notes | | --- | --- | --- | --- | --- | | **Miss → fresh** | [30554890417](https://github.com/MetaMask/metamask-mobile/actions/runs/30554890417) | **~26m 32s** | with-SRP ~18m 31s, without-SRP ~18m 02s (parallel) | Fresh compile + short BS upload (~44s) | | **Hit → reuse + JS repack** | [30570425894](https://github.com/MetaMask/metamask-mobile/actions/runs/30570425894) | **~12m 27s** | **skipped** | Resolve (~49s) + Cirrus setup/repack/upload (~11m 26s); with-SRP repack ~185s, without-SRP ~107s | **Wall-clock savings on the dual-build/upload path: ~14m (~53% faster)** when fingerprint hits and app JS is repacked instead of recompiling both BrowserStack APKs. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [MMQA-2147](https://consensyssoftware.atlassian.net/browse/MMQA-2147) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Android performance BrowserStack fingerprint reuse + repack Scenario: test-only fingerprint hit reuses BS APKs as-is Given a PR with only test file changes (reuse_main_builds / main_branch_only true) And BrowserStack custom_id reuse did not resolve apps And a prior run uploaded android-apk-main-e2e-bs-with-srp and android-apk-main-e2e-bs-without-srp for the same fingerprint When the Build Android Dual Versions workflow runs Then resolve-fingerprint-reuse reports found=true And build-with-srp and build-without-srp are skipped And Upload APKs to BrowserStack re-uploads the reused APKs without repack Scenario: app-change fingerprint hit skips Gradle and repacks JS Given a PR that changes app JS with the same native fingerprint as a prior BS dual build And reuse_main_builds is false When the Build Android Dual Versions workflow runs Then resolve-fingerprint-reuse reports found=true And build-with-srp and build-without-srp are skipped And Upload APKs to BrowserStack runs on Cirrus, repacks both profiles, then uploads And upload summary shows fingerprint-reuse-repack-<sha> Scenario: fingerprint miss still builds fresh Given no prior matching android-apk-main-e2e-bs-* artifacts for this fingerprint When the Build Android Dual Versions workflow runs Then both with-SRP and without-SRP Android builds run fresh And APKs are uploaded to BrowserStack as before Scenario: test-only PR still prefers BrowserStack custom_id reuse Given a PR with only test file changes and main BrowserStack apps resolve When run-performance-e2e runs Then trigger-android-dual-versions is skipped And tests use the resolved main BrowserStack app URLs ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — CI workflow-only change; no app UI. Evidence is Actions logs + timings above. ### **Before** Miss path on this PR ([30554890417](https://github.com/MetaMask/metamask-mobile/actions/runs/30554890417)): dual Gradle builds ran (~18m each in parallel); resolve→upload wall clock **~26m 32s**. ### **After** Hit path on this PR ([30570425894](https://github.com/MetaMask/metamask-mobile/actions/runs/30570425894)): Gradle skipped; `APK source: fingerprint reuse + JS repack`; resolve→upload wall clock **~12m 27s** (~14m / ~53% savings). Performance onboarding + imported-wallet tests passed on the repacked apps. ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### 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** - [ ] 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-83c6732f-f2f5-4168-92f3-c43d2033a60d"><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> <a href="https://cursor.com/background-agent?bcId=bc-83c6732f-f2f5-4168-92f3-c43d2033a60d"><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> </div> [MMQA-2147]: https://consensyssoftware.atlassian.net/browse/MMQA-2147?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: javiergarciavera <javiergarciavera@users.noreply.github.com>
## **Description** Wire interactive actions, sorting, and side filtering into the Perps Pro market view positions/orders panel so traders can manage open positions and orders without leaving Pro mode. **Motivation:** The Pro positions panel previously rendered read-only position and order cards. Figma specifies actionable controls, sort/filter affordances, and a control row layout (sort settings → all sides → ticker checkbox) that should reuse existing Perps flows. **Solution:** - Add `usePerpsProPositionsPanelActions` to centralize panel action wiring with geo/eligibility gates, toasts, and bottom sheets. - **Positions tab actions:** Enable Close, Reverse, and Share on each position card; add editable TP/SL and isolated-margin rows (value text and edit icon share one press target); wire **Close all** on the aggregate unrealized P&L summary. - **Orders tab:** Keep order cards read-only (no size/price/TP-SL edit affordances); enable per-order **Cancel** only for cancelable synthetic orders. - **Sort:** Add a sort settings button that opens a **Sort by** sheet (position value, unrealized P&L, funding rate). Default is position value high to low; tapping the active option toggles high ↔ low before Apply. - **Side filter:** Add an **All sides** button that opens a **Filter by** sheet (all types, long, short) with Apply; button label updates to Long/Short when filtered. - **Control row layout:** Sort settings icon on the left, All sides button next to it, then the `$TICKER only` checkbox. - Reuse existing navigation and sheets: close position, flip/reverse confirm, close-all sheet, TP/SL screen with `usePerpsTPSLUpdate`, and adjust-margin sheet for isolated positions. - Add test IDs, locale strings, and unit tests for actions, sort, side filter, and panel integration. ## **Changelog** CHANGELOG entry: Added position and order actions, sorting, and side filtering to the Perps Pro market view positions panel ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3596 ## **Manual testing steps** ```gherkin Feature: Perps Pro positions panel actions and filters Scenario: Close a position from the Pro market view Given I have an open Perps position and I am on the Pro market view Positions tab When I tap Close on a position card Then the close position flow opens for that position Scenario: Reverse a position from the Pro market view Given I have an open Perps position and I am on the Pro market view Positions tab When I tap Reverse on a position card Then the flip/reverse confirmation sheet is shown Scenario: Edit TP/SL by tapping the value or edit icon Given I have an open Perps position and I am on the Pro market view Positions tab When I tap the TP/SL value text or edit icon on a position card Then the TP/SL screen opens and saving updates the position Scenario: Edit margin on an isolated position Given I have an open isolated-margin Perps position and I am on the Pro market view Positions tab When I tap the margin value text or edit icon on a position card Then the adjust margin sheet opens with add/remove options Scenario: Sort positions by unrealized P&L Given I have multiple open Perps positions and I am on the Pro market view Positions tab When I tap the sort settings icon, select Unrealized P&L, and tap Apply Then positions are ordered by unrealized P&L high to low Scenario: Toggle sort direction for the active sort field Given the sort sheet is open with Position value selected When I tap Position value again and tap Apply Then positions are ordered by position value low to high Scenario: Filter positions to long only Given I have both long and short Perps positions and I am on the Pro market view Positions tab When I tap All sides, select Long, and tap Apply Then only long position cards are shown and the button label reads Long Scenario: Cancel an open order Given I have a cancelable open order and I am on the Pro market view Orders tab When I tap Cancel on an order card Then the order is cancelled and a success toast is shown Scenario: Close all positions Given I have one or more open Perps positions and I am on the Pro market view Positions tab When I tap Close all on the unrealized P&L summary Then the close-all positions sheet opens ``` ## **Screenshots/Recordings** N/A — UI changes are incremental (action buttons, editable rows, sort/filter controls on existing cards). Manual verification on iOS/Android simulator recommended. ### **Before** ### **After** Positions and orders panel cards were read-only; Close all was display-only; no sort or side filter controls. <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-07-31 at 11 30 09" src="https://github.com/user-attachments/assets/6d69ee52-2e31-4bb1-988d-8717d541f999" /> <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-07-31 at 11 30 06" src="https://github.com/user-attachments/assets/22525be7-c521-4ed1-8e0d-16833efc1693" /> <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-07-31 at 11 30 02" src="https://github.com/user-attachments/assets/e33018dc-b676-4736-bbce-6d9dd87d31db" /> https://github.com/user-attachments/assets/b09f932b-6b88-42c5-952c-7536b5587091 ## **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] > **Medium Risk** > Touches live trading paths (close, reverse, cancel, close-all, margin, TP/SL) with compliance gating, but reuses established Perps flows rather than new backend logic. > > **Overview** > The Pro market **positions/orders panel** is no longer read-only: position cards get **Close**, **Reverse**, **Share**, tappable **TP/SL** and **isolated margin** edits, and order cards get a working **Cancel** (with all cancel buttons disabled while one cancel is in flight). **Close all** on the aggregate P&L header is wired through the same flows as the rest of Perps. > > **`usePerpsProPositionsPanelActions`** centralizes navigation, compliance/eligibility gates, toasts, and overlays (close-all, flip confirm, adjust margin, geo block) via a **`PerpsProPositionsModalPortal`** so sheets are not clipped by the parent scroll view. > > The Positions tab adds a control row: **sort** (position value, unrealized P&L, funding rate from `usePerpsMarkets`), **long/short side filter**, and the existing ticker-only checkbox. List order and summary P&L respect the active filters; empty states cover side-only filters. Order cards drop decorative edit icons; position row test IDs are keyed by symbol only. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 11bbd9b. 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: Alejandro Garcia Anglada <aganglada@gmail.com>
#34134) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> <!-- 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** <!-- mms-check: type=text required=true --> Fixes browser close/back navigation from Market Insights news articles incorrectly landing on Explore. 1. **Reason:** Market Insights opened articles with `fromTrending: true` only. `BrowserTab.handleClosePress` has special-case return flags (`fromWhatsHappening`, `fromPerps`, etc.) and otherwise navigates to Explore (`TRENDING_VIEW` / `TRENDING_FEED`). Swipe-right worked because the native stack gesture correctly pops the browser screen. 2. **Solution:** Pass `fromMarketInsights: true` when opening the browser from Market Insights, forward it through Browser → BrowserTab, and handle it in `handleClosePress` with `navigation.goBack()` (same pattern as What’s Happening). ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Fixed browser back from Market Insights news returning to Explore instead of Market Insights ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: #34106 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Market Insights news browser back navigation Scenario: browser close button returns to Market Insights Given the user is on Market Insights When the user taps a news source And the article opens in the in-app browser And the user taps the browser close/back button (top-left ArrowLeft) Then the user returns to Market Insights And the user is not redirected to Explore Scenario: swipe right still returns to Market Insights Given the user opened a news article from Market Insights in the in-app browser When the user swipes right to go back Then the user returns to Market Insights ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> ### **Before** Bug recording from [#34106](#34106): browser back from a Market Insights article lands on Explore. https://github.com/user-attachments/assets/72293a69-f9e7-4130-b72d-f1816740b5f0 ### **After** https://github.com/user-attachments/assets/bbf5c1f6-d34f-405c-8da8-682d50e5236f ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [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** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_AGENT_PR_BODY_END --> [Slack Thread](https://consensys.slack.com/archives/C092MDPA0LU/p1785484726532229?thread_ts=1785484726.532229&cid=C092MDPA0LU) <div><a href="https://cursor.com/agents/bc-ee085fae-72ef-5177-bb6b-692a71336c45?cursor_ref=pr_footer&cursor_cta=open_in_web"><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> <a href="https://cursor.com/background-agent?bcId=bc-ee085fae-72ef-5177-bb6b-692a71336c45&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><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> </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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
Migrates send confirmations to appium and removes the detox tests
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
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**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/MMQA-1990
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
NA
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
<!-- 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**
<!-- mms-check: type=checklist required=true -->
<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.
Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [ ] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->
- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches E2E coverage for wallet send/confirm flows (including a real
on-chain submit in the native test) and CI sharding; production app code
is unchanged but regression signal for confirmations depends on these
tests.
>
> **Overview**
> Moves **SmokeConfirmations** send coverage from Detox/smoke stubs into
**Playwright/Appium** specs under
`tests/smoke-appium/confirmations/send/`, and deletes the old
`tests/smoke/confirmations/send/*` files (including skipped BTC/SOL/TRX
placeholders).
>
> The **native ETH** smoke now runs end-to-end on a local Anvil node:
login via `loginToAppPlaywright`, send MAX, confirm, assert
**Confirmed** on Activity, and validate the finalized tx hash in
metrics. The **USDC MAX** flow is rewritten the same way (Anvil +
Accounts API mocks, `currentDeviceDetails`) but still cancels on the
review screen.
>
> **CI** splits `appium-confirmations-*-smoke` into **two parallel
shards** (`total_splits: 2`) on Android and iOS for the added runtime.
>
> `RedesignedSendView.selectEthereumToken` now taps ETH by **asset test
ID** with the same **retry/until amount screen** behavior as ERC20,
replacing the Appium-only network-filter + text tap path.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7b38127. 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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.
type=text Section must contain non-placeholder prose.
type=changelog Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
comment but do not block the PR.
Sections without a directive are checked for structural presence only.
-->
## **Description**
<!-- mms-check: type=text required=true -->
<!--
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?
-->
Adds the TMCU-1209 Homepage Balance Breakdown experiment with two
treatment variants:
- **Icons:** category rows for Money, Tokens, Perpetuals, Predictions,
and DeFi with product-specific icons.
- **Allocation:** a proportional blue-to-slate allocation bar with
matching row indicators.
The treatment variants replace the legacy homepage balance header with a
shared portfolio hero and homepage-local breakdown. The aggregate:
- combines Money, token, Perps, Predict, and DeFi balances in the user's
display currency;
- computes allocation percentages from positive holdings while
preserving debt in the net total;
- combines token and trustworthy Perps baselines into the 24-hour
“Today” delta;
- distinguishes loading, partial-loading, error, ready, and ineligible
states without leaving the hero permanently loading;
- hides slices whose product feature flags are disabled and prevents
their queries, subscriptions, polling, cache invalidation, and
persistence work;
- consumes Predict live-position updates from the shared query cache
instead of opening a duplicate live-price subscription; and
- preserves the existing wallet-home onboarding fallback and routes
every visible row to its existing product destination.
The UI preserves privacy masking across the hero, rows, and allocation
bar; localizes percentages and Money APY values; supplies privacy-aware
accessibility labels and hints; and keeps the treatment hero aligned
with the existing transparent balance layout.
Wallet integration preserves control and unresolved assignments,
network/basic-functionality banners, growth content, discovery pills,
and action-button placement. Empty network banner containers are not
mounted, avoiding control and treatment spacing gaps.
## **Changelog**
<!-- mms-check: type=changelog required=true blocking=true -->
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry: Added a homepage portfolio balance breakdown
experiment.
## **Related issues**
<!-- mms-check: type=issue-link required=true -->
Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-1210
Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-1211
## **Manual testing steps**
<!-- mms-check: type=manual-testing required=true -->
```gherkin
Feature: Homepage balance breakdown experiment
Scenario: Render the Icons variant
Given a wallet with balances in supported portfolio categories
And the Homepage Balance Breakdown experiment is assigned to "icons"
When the user opens the Wallet homepage
Then the aggregate balance is displayed above the category rows
And Money, Tokens, Perpetuals, Predictions, and DeFi show the expected icons
And tapping each visible row opens its existing destination
Scenario: Render the Allocation variant
Given a wallet with balances in supported portfolio categories
And the Homepage Balance Breakdown experiment is assigned to "allocation"
When the user opens the Wallet homepage
Then the allocation bar uses the blue-to-slate category palette
And category rows display matching colored dots
And positive holdings determine the allocation proportions
And DeFi debt reduces the aggregate net balance without producing a negative segment
Scenario: Preserve privacy
Given either treatment variant is active
When the user enables privacy mode
Then the aggregate balance, delta, row balances, and percentages are masked
And the allocation bar is replaced by a neutral track
And accessibility labels do not expose hidden financial values
Scenario: Respect product availability
Given either treatment variant is active
And Money, Perpetuals, Predictions, or DeFi is disabled
When the user opens the Wallet homepage
Then the disabled product row is not displayed
And the remaining eligible categories still contribute to the aggregate balance
Scenario: Preserve banner spacing
Given either the control or a treatment assignment is active
And neither the basic-functionality banner nor network banner is visible
When the user opens the Wallet homepage
Then no empty banner space is reserved above the balance
When a network or basic-functionality banner becomes visible
Then the banner is displayed with the expected spacing
Scenario: Preserve wallet-home onboarding
Given either treatment variant is active
And wallet-home post-onboarding is in progress
When the user opens the Wallet homepage
Then the existing account-group balance is displayed
And the balance breakdown rows remain hidden until onboarding completes
Scenario: Preserve the legacy homepage
Given the experiment is assigned to "control" or has no active assignment
When the user opens the Wallet homepage
Then the existing account-group balance and homepage actions are displayed
And no balance breakdown rows or allocation bar are mounted
```
## **Screenshots/Recordings**
<!-- mms-check: type=screenshot required=true -->
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<img width="378" height="558" alt="Screenshot 2026-07-30 at 12 24 02"
src="https://github.com/user-attachments/assets/b50711a1-a79b-4761-a1e4-1df61565f342"
/>
<img width="383" height="634" alt="Screenshot 2026-07-30 at 12 24 21"
src="https://github.com/user-attachments/assets/617febd8-d146-4e18-af62-7d3649d2f848"
/>
<img width="370" height="626" alt="Screenshot 2026-07-30 at 12 24 29"
src="https://github.com/user-attachments/assets/1748bbe1-fd0e-49f0-a3b0-1396a5370655"
/>
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
<!-- mms-check: type=checklist required=true -->
<!--
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**
> Large wallet-home surface area and new cross-product balance math
could misstate totals or deltas; mitigated by feature flags, extensive
tests, and `enabled` gates on background fetches.
>
> **Overview**
> Introduces the **TMCU-1209** homepage balance breakdown A/B test
(`control` / `icons` / `allocation`) and wires **Wallet** so treatment
arms replace the legacy `AccountGroupBalance` header with a new
aggregate hero plus category rows, while control and unresolved
assignments stay unchanged.
>
> A new **BalanceBreakdown** stack aggregates Money, tokens, Perps,
Predict, and DeFi into slice hooks and `useBalanceBreakdown` (totals,
allocation %, partial load/error hero state, and a combined 24h “Today”
delta from tokens + trustworthy Perps baselines).
**HomepageBalanceBreakdown** renders **icons** or **allocation** layouts
with privacy masking, Money APY, slice navigation, and `Balance
Breakdown Slice Tapped` analytics.
>
> Supporting changes add **`enabled`** (and related) flags on Money
balance, Predict portfolio, Perps live account, and connection polling
so aggregation can skip work when slices are ineligible; **Homepage**
lifts a single `PerpsConnectionProvider` with `isEnabled` tied to the
Perps flag; **NetworkConnectionBannerContent** is split out for banner
layout in treatment vs control.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b1e4792. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- Generated with the help of the pr-description AI skill -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
…l enabled when position price ticks (#34470) chore(runway): cherry-pick fix(social-leaderboard): keep QuickBuy Sell enabled when position price ticks
This comment has been minimized.
This comment has been minimized.
…gs copy and casing (#34491) - fix(settings): update notifications settings copy and casing (#32922) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Updates copy in the Notifications settings screen to match current content guidelines. Section titles now use sentence case (e.g. "Wallet activity" instead of "Wallet Activity"), the wallet activity description no longer mentions rewards, and the "curated" typo in the trading signals description is fixed. Related unit tests are updated to match the new strings. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- 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 notification settings section titles and descriptions ## **Related issues** <!-- mms-check: type=issue-link required=true --> Refs: content update — no linked issue ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Notifications settings copy Scenario: user views updated notification section labels Given the user has notifications enabled in Settings And the app language is set to English When user navigates to Settings > Notifications Then section titles display sentence case (e.g. "Wallet activity", "Trading activity", "Price alerts") And the wallet activity description reads "Buy, sells, transfers, swaps" And the trading signals description reads "Updates from traders and assets you follow, plus curated market news" ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — copy-only change with no visual layout differences. ### **Before** <img width="408" height="216" alt="Screenshot 2026-07-07 at 4 53 57 PM" src="https://github.com/user-attachments/assets/d48bee52-0dfa-4d4d-9ff5-241521e13f01" /> <img width="398" height="339" alt="Screenshot 2026-07-07 at 4 53 52 PM" src="https://github.com/user-attachments/assets/4fa5782e-fe07-453b-a71b-aa6e7bbf451f" /> <img width="421" height="833" alt="Screenshot 2026-07-07 at 4 53 46 PM" src="https://github.com/user-attachments/assets/00f46231-d6f1-4a17-b806-5424777a33c6" /> <img width="426" height="819" alt="Screenshot 2026-07-07 at 4 53 42 PM" src="https://github.com/user-attachments/assets/7529597a-0003-4601-94b0-e6f9a7bb42af" /> ### **After** <img width="416" height="832" alt="Screenshot 2026-07-07 at 4 40 38 PM" src="https://github.com/user-attachments/assets/2e759492-6fd0-4eb8-beb1-21a6dbeb1d19" /> <img width="418" height="827" alt="Screenshot 2026-07-07 at 4 40 32 PM" src="https://github.com/user-attachments/assets/05b8ee5a-b2f3-4e58-ad6c-281a4b122a78" /> <img width="419" height="835" alt="Screenshot 2026-07-07 at 4 40 28 PM" src="https://github.com/user-attachments/assets/a9f437c5-5a1e-4a4c-9e36-ed87038bf9c3" /> <img width="412" height="815" alt="Screenshot 2026-07-07 at 4 40 23 PM" src="https://github.com/user-attachments/assets/feb72d6e-f1f7-42c4-aaaa-b203c2622d21" /> ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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. Made with [Cursor](https://cursor.com) [3e1619d](3e1619d) Co-authored-by: Andrew Cohen <andrew.cohen@consensys.net>
… Pro fees layout (#34489) - fix(perps): fix truncated market names and Pro fees layout cp-8.7.0 (#34476) <!-- 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** Long ETF and equity market names were overlapping the leverage badge and price column in the market list, and the Pro market header title was clipped too aggressively because it used a fixed `maxWidth`. The Pro order summary fees row also cropped the VIP badge and used a different text color than the other summary values. This PR makes the market list title and header identity text shrink around the leverage badge and right-side actions, removes the hardcoded header name width, and adjusts the Pro fees summary row so the VIP badge is fully visible and the fee value matches the other summary values. ## **Changelog** CHANGELOG entry: Fixed truncated market names in Perps market lists and headers, and improved Pro order summary fees layout. ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: Perps market layout truncation Scenario: market list shows long ETF names without overlapping price Given I am on the Perps Markets screen And I open the ETFs category When I view markets with long names such as "iShares MSCI South Korea ETF" Then the leverage badge stays within the row And the price column is not overlapped or clipped Scenario: Pro market header adapts to right-side actions Given I open a long-named market in Pro mode When the header shows wallet, favorite, and Pro toggle actions Then the market name truncates within the available center space And the leverage badge and chevron remain visible Scenario: Pro order summary fees row displays correctly Given I am on a Pro market order form with an active VIP fee discount When I view the order summary fees row Then the VIP badge is fully visible And the fee value uses the same color and weight as margin and liquidation values ``` ## **Screenshots/Recordings** ### **Before** | Scenario | Screenshot | | --- | --- | | Pro order summary fees row | - | | Market list (long ETF names) | <img width="300" alt="Market list ETFs" src="https://github.com/user-attachments/assets/7c735f0a-c676-4816-9c48-c6be0652f439" /> | | Pro market header | <img width="300" alt="Pro market header" src="https://github.com/user-attachments/assets/bca8b008-9b1c-4955-891d-5c564c7e4d5d" /> | ### **After** | Scenario | Screenshot | | --- | --- | | Pro order summary fees row | <img width="300" alt="Pro order summary fees row" src="https://github.com/user-attachments/assets/ff6ac1a3-63aa-4318-be5c-acfa8e32e68d" /> | | Market list (long ETF names) | <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-08-07 at 16 31 30" src="https://github.com/user-attachments/assets/5ee0af4a-845f-4e65-bba7-ad58cc278dc2" /> | | Pro market header | <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-08-07 at 16 31 34" src="https://github.com/user-attachments/assets/0766d1ae-73be-445a-b251-070d523b79a2" /> | ## **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** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. Made with [Cursor](https://cursor.com) Co-authored-by: Cursor <cursoragent@cursor.com> [82ae592](82ae592) Co-authored-by: Alejandro Garcia Anglada <aganglada@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…ications (#34465) - fix: hide foreground wallet_activity notifications (#34411) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Follow-up to #34043. The previous `wallet_activity` filter in the JS `onMessage` path was meant to suppress system banners only while the app is open (so in-app transaction toasts remain the primary surface). In practice it did not achieve cross-platform parity: 1. **Android**: filtering in JS could incorrectly affect delivery when the app was backgrounded / not fully active, instead of only when the app was in the foreground. 2. **iOS**: `AppDelegate.willPresent` always requested a banner via `completionHandler([.banner, ...])`, so the OS still showed a foreground banner regardless of the JS filter. **Solution:** - Gate the JS `wallet_activity` filter on `AppState.currentState === 'active'` so suppression only applies in the true foreground; otherwise return `true` and do not block this path. - On iOS, suppress banner/sound/list for `notification_type == wallet_activity` in `willPresent` (badge only), while still forwarding the message to Firebase for JS. Background / killed delivery is unchanged (that path never hits `willPresent`). ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Fixed wallet activity push notifications so they are only hidden in the foreground and still appear when the app is backgrounded or closed ## **Related issues** <!-- mms-check: type=issue-link required=true --> Refs: https://consensyssoftware.atlassian.net/browse/GE-357 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Hide wallet_activity push banners only while the app is in the foreground Scenario: Wallet activity while the app is open (iOS and Android) Given the user has push notifications allowed And the MetaMask app is in the foreground When a wallet_activity push notification is received Then the in-app transaction toast is shown for progress and completion And no wallet_activity system notification banner is displayed Scenario: Wallet activity while the app is backgrounded (iOS and Android) Given the user has push notifications allowed And the MetaMask app is in the background or closed When a wallet_activity push notification is received Then the OS still delivers the push notification normally Scenario: Non-wallet_activity push while the app is open Given the MetaMask app is in the foreground When a non-wallet_activity push notification is received (e.g. platform) Then the system notification banner is still displayed ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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 cross-platform push presentation paths; wrong gating could hide wallet activity alerts or duplicate them in foreground, but behavior is covered by unit tests and aligns with existing toast UX. > > **Overview** > **`wallet_activity` push banners are now suppressed only while the app is truly in the foreground**, so background/closed delivery still shows OS notifications and in-app transaction toasts stay the primary surface when open. > > On **JS/Android**, `shouldDisplayForegroundPushNotification` now returns `true` unless `AppState.currentState` is `active`, then applies the existing `wallet_activity` filter—avoiding suppression when the app is backgrounded or inactive. Tests mock `AppState` and cover foreground vs `background`/`inactive`. > > On **iOS**, `AppDelegate` `willPresent` still forwards to Firebase for JS but, for `notification_type == wallet_activity`, completes with **badge only** (no banner/sound/list) instead of always requesting a foreground banner. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a3f6506. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [75ed6ce](75ed6ce) Co-authored-by: Baptiste Marchand <75846779+baptiste-marchand@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…34485) - feat: add Pro only reduce only validation cp-8.7.0 (#34381) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Reason for the change: Perps Pro users could hit confusing or blocking errors when using Reduce Only — including cases where Place Order failed without clear guidance, TP/SL stayed visible/active when it should not, and validation banners looked misaligned when messages wrapped to multiple lines. Improvement/solution: This PR adds Pro-only reduce-only validation that runs before submission, shows the TAT-3255 banner copy, disables Place Order for invalid states, hides/clears TP/SL while reduce-only is on, and starts the size input empty. Validation banner icons are vertically centered with the message text for better readability on wrapped copy. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixed Perps Pro Reduce Only validation so invalid orders show clear warnings before submission, and improved validation banner layout. ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3648 and https://consensyssoftware.atlassian.net/browse/TAT-3678 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Perps Pro Reduce Only validation and banner layout Scenario: Reduce only with no open position shows banner and disables Place Order Given I am on the Perps Pro market view for a market where I have no position When I enable "Reduce only" Then I should see a warning banner about having no position to reduce And the "Place order" button should be disabled And the TP/SL row should be hidden Scenario: Reduce only on the wrong side shows banner and disables Place Order Given I am on the Perps Pro market view with an open long position When I select "Short" and enable "Reduce only" Then I should see a warning banner that reduce only must match my position side And the "Place order" button should be disabled And the TP/SL row should be hidden Scenario: Reduce only size larger than closable position shows centered warning banner Given I am on the Perps Pro market view with an open position And I have enabled "Reduce only" When I enter a size larger than my closable position Then I should see a warning banner that the reduce only order is too large And the warning icon should be vertically centered with the banner text And the "Place order" button should be disabled Scenario: Valid reduce only order can be submitted Given I am on the Perps Pro market view with an open position When I enable "Reduce only" on the matching side And I enter a valid size within my closable position Then I should not see a reduce only validation banner And the "Place order" button should be enabled Scenario: TP/SL is hidden and not submitted while reduce only is on Given I am on the Perps Pro market view When I configure TP/SL and then enable "Reduce only" Then the TP/SL row should be hidden And submitting an order should not include TP/SL values Scenario: Limit order clears TP/SL after successful submission Given I am on the Perps Pro market view with a limit order form When I configure TP/SL and successfully place a limit order Then the TP/SL checkbox should reset to unchecked ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- 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/87b3612d-6035-43bb-afa6-d332afa29d3f https://github.com/user-attachments/assets/3e86973b-2fc9-48d8-a058-20ecb2288d7e https://github.com/user-attachments/assets/7a555e65-def8-4b01-aca6-f613723d9b76 ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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** > Changes order submission and validation on the Pro trading path (reduce-only, TP/SL stripping, minimum-notional exemption); well tested but incorrect logic could block valid closes or allow invalid orders. > > **Overview** > **Perps Pro Reduce Only** now validates against the live position before submit: no position, wrong side (same direction as the open leg), or size larger than the position each show a warning banner, disable **Place order**, and hide the TP/SL row. > > Enabling Reduce Only **clears TP/SL** in form state; submitted orders **omit TP/SL** even if stale values remained. New `validateReduceOnlyOrder` drives notices and `isFullClose`; full closes skip the UI minimum-notional check and pass `reduceOnly` / `isFullClose` through to protocol validation. Margin and limit-price errors surface as **banners**; validation is skipped while the position stream loads to avoid stale errors. > > The Pro form replaces the Reduce Only checkbox with row-style **selection indicators** (shared with TP/SL), centers banner alert icons, and adds i18n strings for the three reduce-only messages. Coverage spans hook, form, view, and util tests. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 57b625d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [d6ae2b1](d6ae2b1) Co-authored-by: Matt D. <85914066+geositta@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
This PR updates the change log for 8.7.0. (Hotfix - no test plan generated.)
🚀 RC Builds Ready for Testing
More Info
🛡️ Build Environment
API URLs & Details
Build Flags:
🍒 What's in this RCCherry-picks (6 commits)
Changelog (823 commits from main at RC cut)
AI Test Plan
Executive SummaryRelease Focus: MetaMask Mobile 8.7.0 is a large-scale release delivering a major Perps Pro Mode trading interface, significant Money/Card feature expansions, Bridge/Swaps enhancements, and a sweeping Navbar refactor alongside DeFi positions V2 and Predict feed improvements. Key Changes:
Critical Areas: Navbar refactor — nearly complete rewrite risks breaking navigation headers across all screens, Perps Pro Mode — new order form, order book, position panel, leverage/limit price/margin sheets all introduced simultaneously, Money/Card onboarding and home view — SignUp, Immersve legal clickwrap, MoneyHomeView major refactor, Bridge/Swaps — new post-trade sheet, Robinhood banner, flip quote button, source amount input changes, App.tsx and MainNavigator changes — navigation provider and main navigator both modified, risking deep-link and startup regressions Overall Risk: HIGH Recommendation: Conditional go — the Navbar near-complete rewrite and simultaneous Perps Pro Mode introduction represent the highest regression risk. Full regression pass on navigation headers, all major transaction flows (send, swap, bridge), and Perps trading is required before release. Money/Card onboarding and DeFi V2 paths also need dedicated exploratory coverage. Release Scenarios (16)High Risk Scenarios (10)1. Navigation / NavbarRisk Level: HIGH Why This Matters: app/components/UI/Navbar/index.js was reduced from ~1400 lines to ~37 lines — virtually all navbar factory functions (getWalletNavbarOptions, getBrowserNavbarOptions, getNetworkNavbarOptions, etc.) were removed. Any screen still importing these functions will crash or render an empty header. The test file was also reduced from 712 to 13 lines, meaning automated coverage was also removed. Preconditions:
Test Steps:
Expected Outcomes:
2. Perps Pro Mode — Order Form and Order BookRisk Level: HIGH Why This Matters: usePerpsProOrderForm.ts (1155 lines), usePerpsProSizeInput.ts (517 lines), and PerpsProOrderBookPanel.tsx (852 lines) are all brand-new files. The size input has complex USD/asset denomination switching with BigNumber precision. The order book integrates with PerpsOrderContext to prefill limit prices. These are entirely untested in production. Preconditions:
Test Steps:
Expected Outcomes:
3. Perps Pro Mode — Positions Panel and Order ManagementRisk Level: HIGH Why This Matters: PerpsProPositionsPanel.tsx grew from a 27-line scaffold to 493 lines. usePerpsProPositionsPanelActions.tsx is a new 430-line hook wiring close, reverse, TP/SL, margin, cancel, and edit flows. PerpsProOrderCard.tsx and PerpsProPositionCard.tsx are new. The limit price bottom sheet now accepts restingOrderSize/leverage/reduceOnly props for open-order editing — a new code path not previously tested. Preconditions:
Test Steps:
Expected Outcomes:
4. Perps — Leverage and Margin AdjustmentRisk Level: HIGH Why This Matters: PerpsLeverageBottomSheet.tsx was rewritten from 601 lines to 383 lines, replacing a custom gesture-based slider with the design-system Slider. PerpsAdjustMarginView.tsx was rewritten replacing PerpsSlider with the design-system Slider and adding new validation logic. PerpsSlider.tsx itself was reduced from 434 to 129 lines, replacing the custom Reanimated gesture implementation with a wrapper around the design-system Slider. Preconditions:
Test Steps:
Expected Outcomes:
5. Perps — Mode Selection and NavigationRisk Level: HIGH Why This Matters: perpsModeSwitch.ts grew from 1 to 228 lines with new dropPerpsHomeFromStackHistory logic. openPerpsModeSelection.ts is a new 65-line utility. perpsModeSelectionStorage.ts is new. PerpsModeSelectionView.tsx is a new 186-line screen. The PerpsMarketDetailsView now calls openPerpsModeSelectionIfNeeded before every mode toggle. Navigation stack manipulation is inherently fragile. Preconditions:
Test Steps:
Expected Outcomes:
6. Bridge / Swaps — Post-Trade Flow and Quote InteractionRisk Level: HIGH Why This Matters: PostTradeBottomSheet/index.tsx is a new 60-line component. FlipQuoteButton/index.tsx gained 20 lines of new logic. useAutoUpdateDestToken gained new test coverage indicating behavioral changes. useSourceAmountInput/index.ts is a new 21-line hook. normalizeNumericTextInput.ts is a new 198-line utility used across Bridge and Perps. RobinhoodSwapsBanner.tsx is a new 156-line component. Preconditions:
Test Steps:
Expected Outcomes:
7. App Startup and Deep Link HandlingRisk Level: HIGH Why This Matters: App.tsx gained 88 lines and MainNavigator.js changed significantly (15 additions, 17 deletions). NavigationProvider.tsx was modified. These are the core navigation bootstrap files — any regression here affects every user on every flow. CliLoginPushNudgeListener.tsx was also modified with new logic. Preconditions:
Test Steps:
Expected Outcomes:
8. Money / Card — Home View and OnboardingRisk Level: HIGH Why This Matters: MoneyHomeView.tsx had 92 additions and 70 deletions. SignUp.tsx had 176 additions and 25 deletions. ImmersveLegalClickwrap.tsx is entirely new (118 lines). MoneyEarnings.tsx had 127 additions and 31 deletions. MoneyMetaMaskCard.tsx had 71 additions and 120 deletions — a net reduction suggesting significant restructuring. These are all user-facing financial screens. Preconditions:
Test Steps:
Expected Outcomes:
9. DeFi Positions V2Risk Level: HIGH Why This Matters: DeFiPositionsListV2.tsx (211 lines), DeFiPositionsListItemV2.tsx (155 lines), DeFiProtocolPositionDetailsV2.tsx (111 lines), DeFiProtocolPositionGroupsV2.tsx (68 lines), useDeFiPositionsV2.ts (193 lines), and filter-defi-positions-by-enabled-networks.ts (31 lines) are all brand new. The existing V1 components were also modified. This is a parallel implementation with risk of data mapping errors. Preconditions:
Test Steps:
Expected Outcomes:
10. Card — Immersve Provisioning and FundingRisk Level: HIGH Why This Matters: useImmersveCardProvisioning.ts had 50 additions and 20 deletions. useImmersveSupportedRegions.ts is new (82 lines). CardHomeFooter.tsx is new (88 lines). CardAlertSection.tsx had 62 additions and 33 deletions. useMoneyAccountCardLinkage.tsx had 34 additions and 25 deletions. onChainAllowance.ts is new (43 lines). These are all financial transaction paths. Preconditions:
Test Steps:
Expected Outcomes:
Medium Risk Scenarios (6)1. Predict — Feed and Market ListRisk Level: MEDIUM Why This Matters: usePredictFeedMarketList.ts is a new 376-line hook replacing or supplementing previous feed logic. feedConfig.ts had major changes (236 additions). PredictFeedBanner.tsx is new (72 lines). Multiple World Cup components were deleted, indicating a feed restructuring. The outcomeGrouping.ts had 128 additions and 17 deletions affecting how market outcomes are displayed. Preconditions:
Test Steps:
Expected Outcomes:
2. Toast NotificationsRisk Level: MEDIUM Why This Matters: Toast.tsx had 201 additions and 34 deletions — a major rewrite of the core toast component. BaseNotification/index.tsx had 223 additions and 63 deletions. Toast.constants.ts added 22 new constants. usePerpsToasts.tsx and usePredictToastRegistrations.tsx are new hooks. Toast regressions affect user feedback across all major flows. Preconditions:
Test Steps:
Expected Outcomes:
3. Account Management — Multi-SRPRisk Level: MEDIUM Why This Matters: AccountCell.tsx had 68 additions and 34 deletions — significant restructuring. multiSrp/index.ts had 40 additions and 26 deletions. onboarding/index.ts had 14 additions. AccountRightButton had 20 additions and 15 deletions. These changes affect core account management which is critical for all wallet operations. Preconditions:
Test Steps:
Expected Outcomes:
4. Onboarding AnimationRisk Level: MEDIUM Why This Matters: onboarding_flow_v25.riv replaced v24 (binary file change). card_tilt_v1.4.riv replaced v1.3. OnboardingAnimation.tsx had 31 additions and 2 deletions. FoxAnimation.tsx and FoxLoader.tsx both gained new lines. Rive animation file replacements can cause crashes if the new file has incompatible state machine names referenced in code. Preconditions:
Test Steps:
Expected Outcomes:
5. Network Connection BannerRisk Level: MEDIUM Why This Matters: NetworkConnectionBanner.tsx had 14 additions and 3 deletions, and a new index.ts export was added. This component is shown to all users when offline and any regression here affects the entire user base's offline experience. Preconditions:
Test Steps:
Expected Outcomes:
6. Android App IconRisk Level: MEDIUM Why This Matters: All Android icon files (ic_launcher, ic_launcher_background, ic_launcher_foreground, ic_launcher_round) across all density buckets were changed (binary file changes). NotificationModule.kt had 30 new lines. Icon changes can result in blank icons, distorted icons, or branding inconsistencies that are highly visible to users. Preconditions:
Test Steps:
Expected Outcomes:
Teams Sign-off Status (0/22)Awaiting sign-off (22): Accounts, Assets, BE Trade, Card, Confirmations, Core Platform, Design System, Earn, Engagement, MetaMask Delivery, Mobile Platform, Mobile UX, Money Movement, Networks, Onboarding, Perps, Predict, Product Safety, Social & AI, Swaps and Bridge, Transactions, Wallet Integrations Excluded Features - Feature Flags Disabled (99)The following features are disabled via feature flags and should NOT be tested:
Generated by AI Test Plan Analyzer (claude-sonnet-4-6) at 2026-08-08T00:48:23.512Z AI generated test plan (JSON): Available as artifact |
…lur with trailing decimal (#34482) - fix(perps): commit cleaned USD amount on blur with trailing decimal cp-8.7.0 (#34421) <!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> <!-- 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? --> - Fix Pro order form USD size blur so `orderForm.amount` is updated when a trailing decimal is removed (e.g. `"12."` → `"12"`). - Align `commitUsdAmount` with limit-price blur by comparing canonical strings instead of numeric equality. - Strengthen unit tests to cover the real onChange → parent echo → blur flow. ## Motivation When a user types a USD size with a trailing decimal separator and blurs the field, the UI correctly finalizes the draft (e.g. `"12."` displays as `"12"`), but `commitUsdAmount` could skip updating canonical order state. **Why:** `onChange` already commits the intermediate value (`"12."`) to `orderForm.amount`. On blur, finalization produces `"12"`, but `BigNumber("12").eq(BigNumber("12."))` is `true`, so `setAmount` was not called again. The field looked correct while `orderForm.amount` still contained the trailing separator. ## Solution - Replace BigNumber-based skip logic in `commitUsdAmount` with string equality so `"12."` and `"12"` are treated as different canonical values. - Preserve existing empty-amount behavior (`""` vs `"0"`) with an explicit empty check so denomination toggles and zero handling are unchanged. - Update the trailing-decimal blur test to rerender with `usdAmount: "12."` after `onChange`, and add a case asserting blur does not re-commit when the amount is already finalized. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- 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** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3688 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Pro order form USD size blur finalization Scenario: user blurs a USD size input with a trailing decimal separator Given the user is on the Perps Pro order form And the USD size field is focused And the canonical order size is not already "12." When the user types "12." into the USD size field And the user blurs the USD size field Then the USD size field displays "12" And the canonical order size is "12" And the canonical order size does not contain a trailing decimal separator ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> N/A - no visible UI change. The field displayed the finalized value before and after; the fix aligns canonical orderForm.amount with the finalized draft. ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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] > **Low Risk** > Localized change to Perps Pro size input commit logic with added unit tests; no auth, security, or broad architectural impact. > > **Overview** > Fixes a mismatch where the Pro order form **USD size field** looked finalized on blur but **`orderForm.amount`** could still hold a trailing decimal (e.g. `"12."`). > > **`commitUsdAmount`** no longer treats `"12."` and `"12"` as the same via `BigNumber` equality. It compares **canonical strings** first, with **`isEmptyUsdAmount`** so `""` vs `"0"` behavior stays the same for empty/zero handling. > > Tests now **rerender** with parent-echoed `usdAmount: "12."` before blur, and add a case that blur does not call **`setAmount`** when the amount is already `"12"`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit fcad6d3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [c36685e](c36685e) Co-authored-by: Matt D. <85914066+geositta@users.noreply.github.com>
🔍 Smart E2E Test Selection⏭️ Smart E2E selection skipped - PR targets a release or stable branch (release/* or stable) All E2E tests pre-selected. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Caution MetaMask internal reviewing guidelines:
|
🚀 v8.7.0 Testing & Release Quality Process
Hi Team,
As part of our new MetaMask Release Quality Process, here’s a quick overview of the key processes, testing strategies, and milestones to ensure a smooth and high-quality deployment.
📋 Key Processes
Testing Strategy
Conduct regression and exploratory testing for your functional areas, including automated and manual tests for critical workflows.
Focus on exploratory testing across the wallet, prioritize high-impact areas, and triage any Sentry errors found during testing.
Validate new functionalities and provide feedback to support release monitoring.
GitHub Signoff
Issue Resolution
Cherry-Picking Criteria
🗓️ Timeline and Milestones
✅ Signoff Checklist
Each team is responsible for signing off via GitHub. Use the checkbox below to track signoff completion:
Team sign-off checklist
This process is a major step forward in ensuring release stability and quality. Let’s stay aligned and make this release a success! 🚀
Feel free to reach out if you have questions or need clarification.
Many thanks in advance
Reference