[pull] main from MetaMask:main#296
Merged
Merged
Conversation
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
Changes:
- invalidate eth query cache before checking balance
- invalidate eth query cache before placing an order (to avoid nonce
collisions)
- optimistic updates for balance, since balance changes take a couple of
seconds after placing an order successfully
## **Changelog**
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry: null
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
https://github.com/user-attachments/assets/f3e0c4e3-c3c3-45fc-b5f8-d43f3c92b57e
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds optimistic balance updates with short-lived caching and
invalidates network query cache before balance checks and order
placement; extends provider interface with name/chainId.
>
> - **PredictController**:
> - Introduces `PredictBalance` and changes `state.balances` to cache `{
balance, validUntil }` per `providerId/address`.
> - Adds `invalidateQueryCache(chainId)` using
`NetworkController.blockTracker.checkForLatestBlock` and calls it before
`placeOrder` and fresh `getBalance` fetches.
> - Implements optimistic balance updates after successful `placeOrder`
(BUY decreases, SELL increases) with 5s validity; `getBalance` caches
for 1s.
> - **Providers**:
> - Extends `PredictProvider` interface with readonly `name` and
`chainId`.
> - `PolymarketProvider` exposes `name = "Polymarket"` and `chainId =
137`.
> - **Selectors/UI**:
> - Updates selectors to read nested
`balances[providerId][address].balance`.
> - `usePredictPlaceOrder` removes post-order balance reload.
> - **Tests**:
> - Adds tests for query-cache invalidation, optimistic balance updates,
and balance caching.
> - Verifies provider interface properties and updates selector tests
accordingly.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
acb7c02. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Remove PPOM local validations. All validations now occur through the security alerts API. We were relying on the local PPOM validations as a fallback, but reality is that based on the data, it was not providing any meaningful support (with only users rejecting 10 transactions that were validated by the local PPOM implementation with a Malicious outcome in the last 3 months) Furthermore this was always the plan from the moment that we released the security alerts API. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: MetaMask/mobile-planning#2300 ## **Manual testing steps** Test a malicious transaction (can use our test dapp) and see that Blockaid warnings are still being displayed. ## **Screenshots/Recordings** Nothing should change. PPOM validations should still be done through the API (as they are right now) ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Removes all local PPOM code and dependencies, switches confirmations to use only the Security Alerts API, adds a migration to clear PPOM storage, and cleans up related CI/config paths and tests. > > - **Confirmations/Security Alerts**: > - Replace PPOM fallback with API-only validation in `ppom-util`; remove controller-based flow and error fallback. > - Drop `blockaidVersion` plumbing from alert reporting and tests. > - **Engine/Core**: > - Remove `PPOMController` (init, messenger, constants, types, wiring) from `Engine`. > - Delete PPOM storage backend and `PPOMView`; remove `<PPOMView />` usage in `App.tsx`. > - **Data/State**: > - Strip `PPOMController` from engine state, Sentry filters, log snapshots, and initial background state. > - **Migrations**: > - Add migration `107` to clear MMKV `PPOMDB` storage; include tests. > - **Build/CI/Tooling**: > - Remove PPOM build step and paths from scripts; drop `BLOCKAID_FILE_CDN` env in workflows. > - Update cache key script to exclude `ppom/`; clean ignores and CODEOWNERS. > - **Dependencies**: > - Remove PPOM-related packages (`@metamask/ppom-validator`, `react-native-blob-jsi-helper`, `react-native-webview-invoke`, `crypto-js` types) and Podfile entry; adjust yarn lock. > - **Config**: > - Remove PPOM env vars from `.js.env.example`; clean eslint/prettier ignores. > - **Tests**: > - Update unit tests to rely on API validation and expectations; delete PPOM-specific tests. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d21bfb1. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Curtis David <Curtis.David7@gmail.com>
#22078) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Added crypto currency metadata fields to mobile ramp event tracking across Buy/Sell (Aggregator) and Deposit flows to improve analytics insights. ## Changes ### Type Definitions - **Aggregator Events** (`app/components/UI/Ramp/Aggregator/types/analytics.ts`): Added optional fields to 18 event interfaces: - `currency_destination_symbol?`: Symbol of the destination cryptocurrency (e.g., "ETH", "USDC") - `currency_destination_network?`: Network name of the destination (e.g., "Ethereum Mainnet", "Polygon") - `currency_source_symbol?`: Symbol of the source cryptocurrency for sell flows - `currency_source_network?`: Network name of the source for sell flows - **Deposit Events** (`app/components/UI/Ramp/Deposit/types/analytics.ts`): Added optional fields to 7 event interfaces: - `currency_destination_symbol?`: Symbol of the destination cryptocurrency - `currency_destination_network?`: Network name of the destination ### Implementation #### Aggregator Flow Updated event tracking in the following files to include new metadata fields: - `Quotes.tsx`: 10 events (quotes requested, expanded, provider clicked/selected, quote errors) - `BuildQuote.tsx`: 2 events (onramp/offramp quotes requested) - `useHandleSuccessfulOrder.ts`: 2 events (purchase submitted) - `OrderDetails.tsx`: 2 events (purchase details viewed) Data source: `order?.cryptoCurrency?.network?.shortName` and `order?.cryptoCurrency?.symbol` #### Deposit Flow Updated event tracking in the following files to include new metadata fields: - `BuildQuote.tsx`: 3 events (order proposed, selected, failed) - `TokenSelectorModal.tsx`: 1 event (token selected) - `useDepositRouting.ts`: 1 event (transaction confirmed) - reverted to not include new fields - `getDepositAnalyticsPayload.ts`: 2 events (transaction completed, failed) - reverted to not include new fields Data source: `selectedCryptoCurrency?.network?.name` or `selectedToken?.network?.name` and corresponding symbol fields ## Impact - Enhanced analytics data collection for ramp transactions - No breaking changes - all new fields are optional - Improved visibility into which networks and currencies users are interacting with <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: added metadata to some analytics events ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TRAM-2770 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds token symbol and network metadata to Buy/Sell Aggregator and Deposit analytics events, with new hooks to resolve network names and updated tests. > > - **Analytics schema**: > - Extend `Aggregator` and `Deposit` event interfaces to optionally include `currency_*_symbol` and `currency_*_network`. > - **Aggregator (Buy/Sell)**: > - Emit new fields in `BuildQuote.tsx`, `Quotes.tsx`, `OrderDetails.tsx`, and `useHandleSuccessfulOrder.ts` across quotes requested/received/expanded, provider clicked/selected, quote error, purchase submitted/details viewed. > - Add `useAggregatorOrderNetworkName` (handles CAIP/EVM/hex) to derive readable network names. > - **Deposit**: > - Emit new fields in `BuildQuote.tsx`, `TokenSelectorModal.tsx`, `useDepositRouting.ts`, and `getDepositAnalyticsPayload.ts` for order proposed/selected/failed, token selected, transaction confirmed/completed/failed. > - Add `useDepositCryptoCurrencyNetworkName` and `useDepositOrderNetworkName` to resolve network names. > - **Tests**: > - Update unit tests across Aggregator and Deposit views/hooks to validate presence of new analytics fields and network resolution logic. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit abe73ae. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Updated the Toast component background color from `background.muted` (transparent) to `background.section` (opaque) to improve visibility and match design specifications. This was introduced in [21942](https://github.com/MetaMask/metamask-mobile/pull/21942/files#diff-3a77a02f68287c5db97fcab49cee7ca3303235de3cfc9ec9d7fd36e2c7853f11R23). This change ensures toasts have proper contrast against different backgrounds. The transparent background was causing readability issues when toasts appeared over various content. ## **Changelog** CHANGELOG entry: Fixed toast background color to improve visibility ## **Related issues** Fixes: N/A ## **Manual testing steps** ```gherkin Feature: Toast visibility Scenario: User views a toast notification Given the app is open on any screen When a toast notification appears Then the toast should have an opaque background And the toast content should be clearly visible against any background content ``` ## **Screenshots/Recordings** ### **Before** Toast had transparent background (background.muted) which could cause visibility issues <img width="476" height="436" alt="Screenshot 2025-11-04 at 2 32 37 PM" src="https://github.com/user-attachments/assets/d0611093-1a6b-49da-b0dc-e264ab0f6575" /> ### **After** Toast now has opaque background (background.section) ensuring consistent visibility https://github.com/user-attachments/assets/3720778b-7bd4-4386-a08c-7d0ecdf25c96 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Switches Toast background to `colors.background.section` and updates the Storybook story to use typed `ToastOptions` with a Tailwind-wrapped container. > > - **Toast styles (`Toast.styles.ts`)**: > - Change background from `colors.background.muted` to `colors.background.section`. > - **Storybook (`Toast.stories.tsx`)**: > - Use typed `ToastOptions` and pass a single `toastOptions` object (includes `variant` and `hasNoTimeout`). > - Wrap story UI in a Tailwind-styled `View` container for layout. > - Modernize story config: type with `Meta`, rename to `Default`, and clean up decorators. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e2817a6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description**
This PR introduces a new reusable `ButtonFilter` component to the
component library.
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes: N/A
## **Manual testing steps**
```gherkin
Feature: ButtonFilter component in Storybook
Scenario: developer views ButtonFilter stories
Given developer has started Storybook
When developer navigates to "Components Temp / Buttons / ButtonFilter"
Then developer should see "Default", "Active", and "FilterGroup" variants
And should be able to toggle isActive state via controls
And should be able to change label text via controls
```
## **Screenshots/Recordings**
### **Before**
### **After**
https://github.com/user-attachments/assets/bddb654d-b745-4e11-85cc-19fad493e258
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds a temporary `ButtonFilter` component with active/inactive
styling, Storybook stories, tests (with snapshots), and documentation.
>
> - **Components**:
> - Add `ButtonFilter` (`ButtonFilter.tsx`, `ButtonFilter.types.ts`)
with active/inactive states using design system + Tailwind.
> - Export via `components-temp/ButtonFilter/index.ts`.
> - **Storybook**:
> - Add `ButtonFilter.stories.tsx` with `Default`, `Active`, and
`FilterGroup` variants.
> - Register story in `.storybook/storybook.requires.js`.
> - **Tests**:
> - Add `ButtonFilter.test.tsx` and snapshots validating render states,
press handling, disabled state, accessibility label, and style prop
spreading.
> - **Docs**:
> - Add `README.md` with props, usage, and styling details.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1d18e8c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…22107) ## **Description** Updated the Open Interest Cap warning design to match the latest design specifications. The warning now uses a more prominent banner style with improved visibility and positioning. **Changes:** 1. Simplified warning message to single concise text 2. Updated styling to use dark background with light text/icon 3. Repositioned warning to appear directly below the chart in scrollable content 4. Action buttons (Long/Short in Market Details, Place Order in Order View) now completely hidden when market is at OI cap ## **Changelog** CHANGELOG entry: Updated Open Interest Cap warning design with improved styling and positioning ## **Related issues** Fixes: [Issue number if available] ## **Manual testing steps** ```gherkin Feature: Open Interest Cap Warning Display Scenario: User views market at OI cap Given user is on a market details screen where the market has reached its open interest cap When the page loads Then user sees a banner warning below the chart with text "Open interest cap reached. New positions cannot be opened at this time." And the Long/Short action buttons are hidden Scenario: User attempts to place order at OI cap Given user navigates to the order view for a market at OI cap When the order form loads Then user sees the OI cap warning banner And the Place Order button is hidden Scenario: User views market not at OI cap Given user is on a market details screen where the market is not at capacity When the page loads Then no OI cap warning is displayed And the Long/Short buttons are visible and functional ``` ## **Screenshots/Recordings** ### **Before** - Warning used inline variant with warning background color - Warning text had title + description structure - Action buttons were visible but disabled when at OI cap - Warning positioned outside scrollable content <img width="397" height="822" alt="image" src="https://github.com/user-attachments/assets/370d9950-a35c-4650-a2b3-5301a2eaf595" /> ### **After** - Warning uses banner variant with dark background and light text/icon - Single concise warning message - Action buttons completely hidden when at OI cap - Warning positioned below chart in scrollable content with proper margins <img width="539" height="1138" alt="image" src="https://github.com/user-attachments/assets/7be19520-20ae-4722-b56b-4e4494d288ed" /> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Redesigns the OI cap warning (banner style, simplified text) and hides Market Hours and all trading actions (Long/Short, Place Order) when a market is at capacity. > > - **Perps UI/UX**: > - **Market Details (`PerpsMarketDetailsView`)**: Adds `PerpsOICapWarning` banner below chart; hides `PerpsMarketHoursBanner` and Long/Short footer buttons when `isAtOICap`. > - **Order View (`PerpsOrderView`)**: Shows `PerpsOICapWarning` banner when at cap; hides fixed Place Order button when at cap; removes inline warning. > - **Component (`PerpsOICapWarning`)**: > - Styling updates (banner background, larger default icon, centered layout, rounded corners); remove title/description split and warning colors; render single i18n message. > - Tests updated to assert i18n string and custom `testID` usage. > - **Localization**: > - Simplifies OI cap strings in `locales/languages/en.json` to a single `perps.order.validation.oi_cap_reached` message; removes unused description/market copy. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit cb7cc07. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Fixes bug where positions and orders were not being passed into watchlist press event as navigation params. This cause a buggy behavior on the MarketDetails screen where positions/orders weren't always present, and would delay or sometimes fail to load entirely on the market detail view. ## **Changelog** CHANGELOG entry: Pass order and position subscription data into watchlist navigation params ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TAT-1982 ## **Manual testing steps** Navigation behavior should be the same from HomeScreen regardless of whether it originates from Your Positions or Watchlist ## **Screenshots/Recordings** https://github.com/user-attachments/assets/8afb5c25-8756-4c62-b2a9-e59c92b79837 ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Derives and passes `initialTab` to MarketDetails based on live positions/orders when navigating from Watchlist, with tests updated to mock hooks and assert new params. > > - **Perps Watchlist navigation**: > - Subscribe to `usePerpsLivePositions` and `usePerpsLiveOrders` and derive `initialTab` (`'position'` | `'orders'` | `undefined`) in `PerpsWatchlistMarkets.tsx`. > - Pass `{ market, initialTab }` in navigation to `Routes.PERPS.ROOT -> MARKET_DETAILS`. > - **Tests**: > - Mock Perps stream hooks and update expectations to include `initialTab` in navigation params. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9306127. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description**
This PR fixes a crash that occurred when users tapped on the Polymarket
terms link in the Predict Unavailable bottom sheet. The crash was caused
by nested `Text` components where the inner `Text` had an `onPress`
handler, leading to deep recursion in React Native's layout measurement
(`facebook::react::progressState`).
**Changes:**
- Replaced external browser navigation with in-app WebView navigation
for Polymarket terms
- Refactored the terms link layout by wrapping the entire text block
with `TouchableOpacity` instead of having nested `Text` components with
`onPress`
- Added `InteractionManager.runAfterInteractions` to ensure smooth
navigation transitions
- Added new localization string `predict.unavailable.webview_title` for
the WebView title
- Updated all unit tests to match the correct localized strings
**Why in-app WebView?**
- Provides a consistent user experience by keeping users within the app
- Follows the pattern used for other terms/legal documents in the app
- Avoids potential issues with external browser availability or
configuration
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes: <!-- Add issue number if available -->
## **Manual testing steps**
```gherkin
Feature: Polymarket terms link in Predict Unavailable screen
Scenario: user taps on Polymarket terms link
Given user is in a region where Predict is unavailable
And user sees the "Unavailable in your region" bottom sheet
When user taps on the "See Polymarket terms" text
Then the app should navigate to an in-app WebView
And the WebView should display "https://polymarket.com/tos"
And the WebView title should be "Polymarket Terms"
And the app should not crash
```
## **Screenshots/Recordings**
https://github.com/user-attachments/assets/b8c0cc3c-0d5d-4bd8-b214-c6f90df8e324
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Opens Polymarket terms in an in-app WebView with deferred navigation
and refactors the link to a touchable element; updates tests and i18n.
>
> - **PredictUnavailable
(`app/components/UI/Predict/components/PredictUnavailable/PredictUnavailable.tsx`)**:
> - Replace external `Linking.openURL` with in-app navigation to
`Webview > SimpleWebview` via `useNavigation` and
`InteractionManager.runAfterInteractions`.
> - Refactor terms link: wrap content in `TouchableOpacity`, add
`testID` `polymarket-terms-link`, remove nested `Text` onPress.
> - Minor cleanup: remove `Linking` import; keep bottom sheet behavior
unchanged.
> - **Tests (`PredictUnavailable.test.tsx`)**:
> - Update to mock `InteractionManager.runAfterInteractions` and
navigation; assert deferred callback and navigation params (`url`,
`title`).
> - Adjust expectations to new localized strings and presence of
`polymarket-terms-link`.
> - Remove external Linking assertions.
> - **Localization (`locales/languages/en.json`)**:
> - Add `predict.unavailable.webview_title`="Polymarket Terms"; confirm
updated copy for title/description/link.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
63c0b10. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#22086) ## **Description** This PR implements market-level whitelisting and blacklisting for Perps HIP-3 markets, providing granular control over which perpetual trading markets are shown to users. ### What is the reason for the change? Improvement from the initial remote feature flag implementation in PR #21823. While the initial PR established the remote feature flag infrastructure for Perps, we needed more granular control beyond simple DEX-level filtering. Specifically: 1. Users need the ability to enable/disable specific markets (e.g., "BTC", "xyz:XYZ100") rather than all markets from a DEX 2. Operators need both whitelist (enable only specific markets) and blacklist (block specific markets) capabilities 3. The system must support wildcard patterns for efficient bulk operations (e.g., "xyz:*" to enable/block all markets from a DEX) ### What is the improvement/solution? **Migration from DEX-level to Market-level Filtering:** - **Before**: Single `MM_PERPS_HIP3_ENABLED_DEXS` variable for whitelisting entire DEXs - **After**: Two variables for granular market control: - `MM_PERPS_HIP3_ALLOWLIST_MARKETS` - Whitelist (empty = enable all, non-empty = only show these) - `MM_PERPS_HIP3_BLOCKLIST_MARKETS` - Blacklist (empty = block none, non-empty = hide these) **Key Features:** 1. **Wildcard Pattern Support** - `"xyz:*"` or `"xyz"` - All markets from xyz DEX - `"xyz:TSLA"` - Specific market - `"BTC"` - Main DEX market 2. **Remote Feature Flag Integration** - LaunchDarkly provides remote configuration - Local env variables serve as fallback - "Sticky remote" pattern: once remote config loads, never downgrade to fallback 3. **Automatic Cache Invalidation & Reconnection** - `hip3ConfigVersion` increments when config changes - ConnectionManager monitors version via Redux - Automatically triggers cache clearing and provider reconnection - No manual intervention required 4. **Performance Optimizations** - Pattern caching (pre-compiled regex stored in Map) - Separate cache keys for filtered vs unfiltered data - `skipFilters` parameter for administrative queries **Architecture:** ``` LaunchDarkly � RemoteFeatureFlagController � PerpsController � (hip3ConfigVersion++) ConnectionManager � (monitors version change) Clear caches + Reconnect � HyperLiquidProvider � (applies filters) Market list with patterns applied ``` ## **Changelog** CHANGELOG entry: Added market-level whitelisting and blacklisting for Perps HIP-3 markets with wildcard pattern support and automatic reconnection on configuration changes ## **Related issues** Related to: #21823 (Initial remote feature flag implementation for Perps) This PR splits out the market filtering functionality (Task #2) from the original feature flag work. ## **Manual testing steps** ```gherkin Feature: Perps HIP-3 Market Filtering Scenario: Whitelist enables all markets when empty Given MM_PERPS_HIP3_ENABLED is true And MM_PERPS_HIP3_ALLOWLIST_MARKETS is empty And MM_PERPS_HIP3_BLOCKLIST_MARKETS is empty When user opens the Perps tab Then all available markets should be visible And markets from both main DEX and HIP-3 DEXs should appear Scenario: Whitelist restricts to specific markets Given MM_PERPS_HIP3_ENABLED is true And MM_PERPS_HIP3_ALLOWLIST_MARKETS is "BTC,ETH,xyz:TSLA" And MM_PERPS_HIP3_BLOCKLIST_MARKETS is empty When user opens the Perps tab Then only BTC, ETH, and xyz:TSLA markets should be visible And all other markets should be hidden Scenario: Whitelist with wildcard enables all markets from DEX Given MM_PERPS_HIP3_ENABLED is true And MM_PERPS_HIP3_ALLOWLIST_MARKETS is "xyz:*,BTC" And MM_PERPS_HIP3_BLOCKLIST_MARKETS is empty When user opens the Perps tab Then BTC market should be visible And all markets from xyz DEX should be visible And markets from other HIP-3 DEXs should be hidden Scenario: Blacklist blocks specific markets Given MM_PERPS_HIP3_ENABLED is true And MM_PERPS_HIP3_ALLOWLIST_MARKETS is empty And MM_PERPS_HIP3_BLOCKLIST_MARKETS is "xyz:TSLA,BTC" When user opens the Perps tab Then xyz:TSLA market should be hidden And BTC market should be hidden And all other markets should be visible Scenario: Blacklist with wildcard blocks all markets from DEX Given MM_PERPS_HIP3_ENABLED is true And MM_PERPS_HIP3_ALLOWLIST_MARKETS is empty And MM_PERPS_HIP3_BLOCKLIST_MARKETS is "xyz:*" When user opens the Perps tab Then all markets from xyz DEX should be hidden And markets from main DEX and other HIP-3 DEXs should be visible Scenario: Remote feature flag overrides local configuration Given local env has MM_PERPS_HIP3_ALLOWLIST_MARKETS="BTC" And LaunchDarkly is configured with perpsAllowlistMarkets="ETH,SOL" When app initializes and fetches remote feature flags Then only ETH and SOL markets should be visible And BTC market should be hidden (remote overrides local) Scenario: Automatic reconnection on configuration change Given user has Perps tab open with markets loaded And LaunchDarkly configuration is "BTC,ETH" When LaunchDarkly configuration changes to "SOL,AVAX" Then hip3ConfigVersion should increment And ConnectionManager should detect the change And app should automatically clear caches And app should reconnect to providers And only SOL and AVAX markets should be visible without manual refresh Scenario: Fallback to local configuration when remote unavailable Given LaunchDarkly is unreachable And local env has MM_PERPS_HIP3_ALLOWLIST_MARKETS="BTC,ETH" When user opens the Perps tab Then local configuration should be used as fallback And only BTC and ETH markets should be visible Scenario: Shorthand wildcard notation Given MM_PERPS_HIP3_ALLOWLIST_MARKETS is "xyz" (without :*) When user opens the Perps tab Then "xyz" should be interpreted as "xyz:*" And all markets from xyz DEX should be visible ``` ## **Screenshots/Recordings** ### **Before** N/A - This is a configuration-based feature without UI changes ### **After** N/A - This is a configuration-based feature without UI changes ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ## Technical Implementation Details ### Environment Variables **New variables added:** ```bash # HIP-3 Feature Flags (remote override with local fallback) export MM_PERPS_HIP3_ENABLED="true" export MM_PERPS_HIP3_ALLOWLIST_MARKETS="" # Whitelist: Empty = enable all markets export MM_PERPS_HIP3_BLOCKLIST_MARKETS="" # Blacklist: Empty = no blocking ``` **Removed variables:** ```bash # OLD - Removed export MM_PERPS_HIP3_ENABLED_DEXS="" # Replaced by market-level filtering export MM_PERPS_ENABLED_DEXS="" # Replaced by market-level filtering ``` ### Pattern Matching Examples | Pattern | Matches | Description | |---------|---------|-------------| | `"BTC"` | `BTC` (main DEX) | Exact match on main DEX market | | `"xyz:TSLA"` | `xyz:TSLA` | Exact match on HIP-3 market | | `"xyz:*"` | `xyz:TSLA`, `xyz:AAPL`, etc. | All markets from xyz DEX | | `"xyz"` | `xyz:TSLA`, `xyz:AAPL`, etc. | Shorthand for `"xyz:*"` | | `""` (empty) | All markets | Discovery mode (whitelist) or no blocking (blacklist) | ### Pattern Matching Implementation **Type Safety:** - `CompiledPatternMatcher` - Type alias for pattern matchers (string for exact match, RegExp for wildcards) - `CompiledPattern` - Interface combining original pattern with compiled matcher **Compilation Strategy:** ```typescript // Pattern → Compiled Matcher "xyz:*" → /^xyz:/ (prefix regex) "xyz" → /^xyz:/ (shorthand → prefix regex) "xyz:TSLA" → "xyz:TSLA" (exact string match - fastest) ``` **Performance Optimization:** 1. All patterns pre-compiled at provider initialization via `recompileAllPatterns()` 2. Stored in typed arrays (`CompiledPattern[]`) for direct iteration 3. Eliminates repeated `compilePattern()` function call overhead during filtering 4. Better code clarity and type safety with pre-compiled matchers 5. Exact matches use string equality (fastest), wildcards use RegExp.test() **Filtering Logic in `shouldIncludeMarket()`:** ```typescript // Main DEX markets always included if (dex === null) return true; // Apply whitelist (if non-empty) if (compiledEnabledPatterns.length > 0) { if (!compiledEnabledPatterns.some(p => matches(symbol, p.matcher))) { return false; // Not whitelisted } } // Apply blacklist (if non-empty) if (compiledBlockedPatterns.length > 0) { if (compiledBlockedPatterns.some(p => matches(symbol, p.matcher))) { return false; // Blacklisted } } return true; ``` ### New Selectors - `selectHip3ConfigVersion()` - Returns version number for cache invalidation used by ConnectionManager to detect config changes ### Files Modified **Core Logic (258 lines added):** - `app/components/UI/Perps/controllers/PerpsController.ts` - `refreshHip3ConfigFromRemote()` - Extracts and validates remote config - Version tracking and increment on config change - "Sticky remote" pattern implementation **Provider Implementation (411 lines added):** - `app/components/UI/Perps/controllers/providers/HyperLiquidProvider.ts` - `CompiledPatternMatcher` type - Type alias for pattern matchers - `CompiledPattern` interface - Type for pre-compiled patterns - `recompileAllPatterns()` - Pre-compiles all patterns at initialization - `shouldIncludeMarket()` - Pattern matching logic using pre-compiled patterns - `matchesCompiledPattern()` - Fast pattern matching without Map lookups - `compilePattern()` - Regex compilation and caching - `skipFilters` parameter support - Separate cache keys for filtered/unfiltered data **Selectors:** - `app/components/UI/Perps/selectors/featureFlags/index.ts` - `selectHip3ConfigVersion` - Version selector for monitoring config changes **Connection Management (22 lines added):** - `app/components/UI/Perps/services/PerpsConnectionManager.ts` - Monitors `hip3ConfigVersion` changes via Redux - Triggers automatic cache clearing and reconnection **Tests:** - `app/components/UI/Perps/selectors/featureFlags/index.test.ts` - `selectHip3ConfigVersion` (3 tests) - Validates version tracking - Removed unused selector tests for unused allowlist/blocklist selectors **Configuration:** - `.js.env.example` - Environment variable documentation - `bitrise.yml` - CI/CD environment configuration - `app/core/Engine/controllers/perps-controller/index.ts` - Fallback parsing **Types:** - `app/components/UI/Perps/controllers/types/index.ts` - Updated `PerpsControllerConfig` interface - Added `skipFilters` to `GetMarketsParams` ### Performance Considerations 1. **Pattern Pre-Compilation**: All filter patterns are compiled once at initialization and stored in typed arrays (`CompiledPattern[]`), eliminating repeated `compilePattern()` function calls during market filtering 2. **Type-Safe Pattern Matchers**: Uses `CompiledPatternMatcher` type alias and `CompiledPattern` interface for better documentation and type safety 3. **Optimized Filtering**: `shouldIncludeMarket()` iterates pre-compiled arrays directly with compiled matchers readily available, improving code clarity and maintainability 4. **Separate Cache Keys**: Filtered and unfiltered data cached separately (`"${dex}_raw"` vs `"${dex}_filtered"`) 5. **StreamManager Integration**: Market metadata cached for 5 minutes, prices from WebSocket (real-time) ### Migration Guide **From DEX-level to Market-level Filtering** This PR replaces DEX-level filtering with more granular market-level filtering: **Removed Environment Variables:** ```bash # OLD - No longer used export MM_PERPS_HIP3_ENABLED_DEXS="xyz,abc" # Whitelist entire DEXs export MM_PERPS_ENABLED_DEXS="xyz,abc" # Alternative naming (removed) ``` **New Environment Variables:** ```bash # NEW - Market-level control export MM_PERPS_HIP3_ALLOWLIST_MARKETS="" # Whitelist: empty = all markets (discovery mode) export MM_PERPS_HIP3_BLOCKLIST_MARKETS="" # Blacklist: empty = block nothing ``` **Common Migration Scenarios:** 1. **Enable all markets from specific DEXs (most common):** ```bash # Before: Enable all markets from xyz and abc DEXs MM_PERPS_HIP3_ENABLED_DEXS="xyz,abc" # After: Use wildcard patterns MM_PERPS_HIP3_ALLOWLIST_MARKETS="xyz:*,abc:*" # Or use shorthand (equivalent) MM_PERPS_HIP3_ALLOWLIST_MARKETS="xyz,abc" ``` 2. **Enable specific markets only:** ```bash # Before: Not possible (DEX-level only) # After: List specific markets MM_PERPS_HIP3_ALLOWLIST_MARKETS="BTC,ETH,xyz:TSLA,xyz:AAPL" ``` 3. **Enable all markets except specific ones:** ```bash # Before: Not possible (no blacklist support) # After: Use blacklist (whitelist empty = all markets) MM_PERPS_HIP3_ALLOWLIST_MARKETS="" MM_PERPS_HIP3_BLOCKLIST_MARKETS="xyz:SCAM,abc:RISKY" ``` 4. **Block entire DEX:** ```bash # Before: Omit from ENABLED_DEXS list MM_PERPS_HIP3_ENABLED_DEXS="xyz" # abc implicitly blocked # After: Use blacklist wildcard MM_PERPS_HIP3_ALLOWLIST_MARKETS="" # Enable all MM_PERPS_HIP3_BLOCKLIST_MARKETS="abc:*" # Block abc DEX # Or use shorthand MM_PERPS_HIP3_BLOCKLIST_MARKETS="abc" ``` 5. **Enable all markets (discovery mode):** ```bash # Before: Leave ENABLED_DEXS empty or set to all known DEXs MM_PERPS_HIP3_ENABLED_DEXS="" # After: Leave ALLOWLIST_MARKETS empty MM_PERPS_HIP3_ALLOWLIST_MARKETS="" MM_PERPS_HIP3_BLOCKLIST_MARKETS="" ``` **LaunchDarkly Remote Feature Flags:** The same patterns apply to LaunchDarkly configuration: - `perpsAllowlistMarkets` - Array of market patterns (empty = all markets) - `perpsBlocklistMarkets` - Array of market patterns to block (empty = block nothing) **Example LaunchDarkly Config:** ```json { "perpsHip3Enabled": true, "perpsAllowlistMarkets": ["BTC", "ETH", "xyz:*"], "perpsBlocklistMarkets": ["xyz:SCAM"] } ``` ### Breaking Changes None - This is an additive change with backward-compatible fallback behavior. **Note:** While `MM_PERPS_HIP3_ENABLED_DEXS` is no longer used, removing it from your configuration will not break anything. The new market-level filtering is more flexible and supersedes DEX-level filtering. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Implements market-level allowlist/blocklist filtering for HIP-3 perps with remote-flag overrides, caching, and automatic reconnection via hip3ConfigVersion. > > - **Perps HIP-3 market filtering (allowlist/blocklist)**: > - Add pattern-based filtering (exact, wildcard, DEX shorthand) via `shouldIncludeMarket`, `compileMarketPattern`, etc. in `utils/marketUtils`. > - New parsing util `parseCommaSeparatedString` for LaunchDarkly/env values. > - New env vars in `.js.env.example` and CI (`bitrise.yml`): `MM_PERPS_HIP3_ENABLED`, `MM_PERPS_HIP3_ALLOWLIST_MARKETS`, `MM_PERPS_HIP3_BLOCKLIST_MARKETS` (replace old DEX-level flags). > - **Controller & reconnection**: > - `PerpsController`: ingest remote flags for HIP-3 (enabled/allowlist/blocklist), maintain `hip3ConfigVersion`, and propagate config to provider. > - `PerpsConnectionManager`: monitor `selectHip3ConfigVersion` to clear caches and reconnect on config changes. > - Add `selectHip3ConfigVersion` selector; default state updated to include `hip3ConfigVersion`. > - **Provider & subscriptions**: > - `HyperLiquidProvider`: apply market filtering, add market metadata caching (filtered/unfiltered), support `skipFilters`, and pass HIP-3 config to `HyperLiquidSubscriptionService`. > - `HyperLiquidSubscriptionService`: support HIP-3 on/off, map webData2 (main DEX) vs webData3 (multi-DEX), update feature flags without teardown; remove redundant clearinghouseState path. > - **Types & tests**: > - Extend `GetMarketsParams` with `skipFilters`; update `PerpsControllerConfig` fallbacks. > - Extensive unit tests added/updated for HIP-3 config parsing, filtering, subscriptions, selectors, and connection manager. > - **Misc**: > - Remove unused debug styles in `PerpsTabView.styles.ts`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d93ca77. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** **Problem:** Fee discount was not being displayed in the "Close all positions" view, despite working correctly in the single position close view. This caused users to not see their rewards discount (e.g., -65%) when closing all positions. **Solution:** Added account-level fee discount fetching to `usePerpsCloseAllCalculations` hook, matching the implementation in the single position close flow. The fix includes: 1. **Fee Discount Fetching**: Added `RewardsController.getPerpsDiscountForAccount()` call to fetch user's discount 2. **Discount Application**: Applied discount to MetaMask fees using formula: `adjusted_rate = original_rate * (1 - discount_bips/10000)` 3. **Batch API Migration**: Migrated to batch points estimation API for better performance (N+1 API calls � 2 total calls) 4. **Error Handling**: Preserved `undefined` state for unavailable fees instead of defaulting to 0 **Technical Details:** - Account-level discount applies uniformly to all positions - Per-position fee calculations for accuracy (coin-specific parameters) - Freeze mechanism prevents repeated calculations on WebSocket position updates - Removed `feeDiscountBips` from effect dependencies to prevent recalculation on every update ## **Changelog** CHANGELOG entry: Fixed fee discount not displaying in close all positions view ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TAT-1966 ## **Manual testing steps** ```gherkin Feature: Fee discount display in close all positions Scenario: user views fee discount when closing all positions Given user has an account with fee discount (e.g., 65%) And user has multiple open positions When user navigates to "Close all positions" view Then fee discount badge should display (e.g., "-65%") And fees breakdown should show discounted MetaMask fee rate And estimated points should be calculated correctly ``` ## **Screenshots/Recordings** ### **Before** Fee discount was hardcoded to 0%, not showing user's actual rewards discount. ### **After** Fee discount now displays correctly (e.g., -65%) in the close all positions view, matching the single position close behavior. https://github.com/user-attachments/assets/76fa2bbc-bc1f-4b15-8655-82df18df8f3f ## **Implementation Details** ### Files Changed - `app/components/UI/Perps/hooks/usePerpsCloseAllCalculations.ts` ### Key Changes 1. **Added Fee Discount State** (lines 108-109): ```typescript const [feeDiscountBips, setFeeDiscountBips] = useState<number>(0); ``` 2. **Fetch Account-Level Discount** (lines 137-166): ```typescript useEffect(() => { async function fetchFeeDiscount() { const discountBips = await Engine.context.RewardsController.getPerpsDiscountForAccount(caipAccountId); setFeeDiscountBips(discountBips); } fetchFeeDiscount(); }, [selectedAddress, currentChainId]); ``` 3. **Apply Discount to Fees** (lines 226-252): ```typescript const discountMultiplier = feeDiscountBips > 0 ? 1 - feeDiscountBips / 10000 : 1; const adjustedMetamaskFeeRate = baseFees.metamaskFeeRate * discountMultiplier; const adjustedMetamaskFeeAmount = baseFees.metamaskFeeAmount !== undefined ? baseFees.metamaskFeeAmount * discountMultiplier : undefined; ``` 4. **Batch Points Estimation** (lines 274-295): ```typescript const batchEstimateBody: EstimatePointsDto = { activityType: 'PERPS', account: caipAccountId, activityContext: { perpsContext: perpsContextArray, // Array of all positions }, }; batchPoints = await Engine.context.RewardsController.estimatePoints(batchEstimateBody); ``` 5. **Calculate Discount Percentage** (lines 387-395): ```typescript const avgOriginalMetamaskFeeRate = feeDiscountBips > 0 && avgMetamaskFeeRate > 0 ? avgMetamaskFeeRate / (1 - feeDiscountBips / 10000) : avgMetamaskFeeRate; const avgFeeDiscountPercentage = feeDiscountBips > 0 ? feeDiscountBips / 100 : 0; ``` 6. **Optimized Dependencies** (line 333): - Removed `feeDiscountBips` from effect dependencies to prevent recalculation on every WebSocket position update - Uses ref-based freeze mechanism (`hasValidResultsRef`) to prevent repeated calculations ### Performance Improvements - **Batch API**: Reduced API calls from N+1 (one per position + one for discount) to 2 total calls - **Freeze Mechanism**: Prevents slow points computation from retriggering on WebSocket position updates ## **Known Limitations** There is a separate UI issue with tooltip z-index in the close position views where the "Close position" button appears on top of tooltip modals. This will be addressed in a follow-up PR. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds account-level fee discount to close-all flow, switches rewards to batch estimation, and makes fee rates/price-driven calculations resilient with UI fallbacks. > > - **Perps Close All (hook `usePerpsCloseAllCalculations`)**: > - Fetches and applies account-level discount (`RewardsController.getPerpsDiscountForAccount`) to MetaMask fees; computes original vs discounted rates and avg discount. > - Migrates rewards to batch points estimation; aggregates totals/averages and guards errors; introduces `isFetchingInBackground`. > - Freezes results to avoid WS-induced recomputes; resets on positions/account/discount changes; robust cleanup/race guards. > - Treats fee rates/amounts as optional (`undefined`) and propagates through aggregates; `receiveAmount` falls back when fees unavailable. > - **Perps Close Single (`PerpsClosePositionView`)**: > - Stabilizes calculations using price refs; uses limit price when valid; updates P&L/position value derivations; expands tests for limit validation and price parsing. > - **UI Summary (`PerpsCloseSummary`)**: > - Accepts optional fees/rates; shows `"--"` when unavailable; adds tooltip interactions; updated tests. > - **Types & Fees Hook**: > - Marks `FeeCalculationResult` fee fields as optional; `usePerpsOrderFees` handles undefined rates (no misleading $0), updates caching checks, and tests edge cases. > - **Styling**: > - Ensures close-all footer stays behind overlays (`zIndex`/`elevation`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit bf388c8. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Ramon AC <36987446+racitores@users.noreply.github.com> Co-authored-by: Christopher Ferreira <104831203+christopherferreira9@users.noreply.github.com> Co-authored-by: javiergarciavera <76975121+javiergarciavera@users.noreply.github.com> Co-authored-by: Nick Gambino <35090461+gambinish@users.noreply.github.com> Co-authored-by: George Weiler <georgejweiler@gmail.com> Co-authored-by: Luis Taniça <matallui@gmail.com> Co-authored-by: AxelGes <34173844+AxelGes@users.noreply.github.com> Co-authored-by: Pedro Pablo Aste Kompen <wachunei@gmail.com> Co-authored-by: Kylan Hurt <6249205+smilingkylan@users.noreply.github.com> Co-authored-by: SteP-n-s <stylianos.panagakos@consensys.net> Co-authored-by: Brian August Nguyen <brianacnguyen@gmail.com> Co-authored-by: tommasini <46944231+tommasini@users.noreply.github.com> Co-authored-by: Kevin Bluer <kevin@bluer.com> Co-authored-by: Vince Howard <vincenguyenhoward@gmail.com> Co-authored-by: Curtis David <Curtis.David7@gmail.com> Co-authored-by: Caainã Jeronimo <caainaje@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )