Skip to content

[pull] main from MetaMask:main#507

Merged
pull[bot] merged 3 commits into
Reality2byte:mainfrom
MetaMask:main
Feb 9, 2026
Merged

[pull] main from MetaMask:main#507
pull[bot] merged 3 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Feb 9, 2026

See Commits and Changes for more details.


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

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

abretonc7s and others added 3 commits February 9, 2026 10:14
…25685)

## **Description**

This PR optimizes the token details page by implementing a lightweight
way to display open perps positions without requiring full
PerpsController initialization, and adds one-click Long/Short trading
buttons that navigate directly to the order confirmation flow.

**Problem:** Previously, checking if a user had an open position on a
token required initializing the entire PerpsController with WebSocket
subscriptions, causing slow page loads. Additionally, cached position
data could become stale when users closed positions in the perps
environment. There was also no way to initiate a perps trade directly
from the token details page.

**Solution:** Added a `readOnly` mode pattern that allows querying perps
data (positions, account state, markets) via lightweight HTTP API calls
without WebSocket, wallet, or full controller initialization. Combined
with a `PerpsCacheInvalidator` service that ensures cached data is
refreshed when positions change. Added Long/Short buttons on token
details that navigate through a new `PerpsOrderRedirect` screen to
handle WebSocket initialization and order creation seamlessly.

### Key Changes

**New Hook:**
- `usePerpsPositionForAsset` - Fetches position data via single HTTP API
call with 30-second client-side cache, subscribes to cache invalidation
events

**ReadOnly Mode (Controller/Provider):**
- `getPositions({ readOnly: true, userAddress })` - Query positions
without full init
- `getAccountState({ readOnly: true, userAddress })` - Query account
state without full init
- Uses `createStandaloneInfoClient` for lightweight HTTP-only queries

**Cache Invalidation Service:**
- `PerpsCacheInvalidator` - Singleton service for loosely-coupled cache
invalidation
- Hooks subscribe to invalidation events (`positions`, `accountState`)
- Services (TradingService, AccountService) call `invalidate()` after
successful operations
- Ensures position display is always accurate even after user actions in
perps

**One-Click Trade from Token Details:**
- New `PerpsOrderRedirect` screen - a redirect/loader that initializes
the WebSocket connection inside the Perps stack, calls
`depositWithOrder()`, then navigates to the confirmation screen
- `usePerpsActions` hook - provides `handlePerpsAction(direction)`
callback for Long/Short buttons, navigates to `PerpsOrderRedirect` with
direction and asset params
- `AssetOverviewContent` integrates Long/Short buttons via
`usePerpsActions` when a perps market exists for the token

**Dynamic Button Layout:**
- Token details action buttons (Buy, Sell, Swap, Bridge, etc.) now
include Long/Short when a perps market exists
- Buttons dynamically overflow into a "More" menu when there are too
many actions

**Documentation:**
- Added "ReadOnly Mode (Lightweight Queries)" section to
`docs/perps/perps-architecture.md`
- Added "Cache Invalidation" subsection documenting the pattern

### Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                    PerpsCacheInvalidator                        │
│  (Singleton service)                                            │
├─────────────────────────────────────────────────────────────────┤
│  - invalidate('positions')    → notifies position subscribers   │
│  - invalidate('accountState') → notifies account subscribers    │
│  - subscribe(type, callback)  → returns unsubscribe function    │
└─────────────────────────────────────────────────────────────────┘
          ▲                                    ▲
          │ subscribe                          │ invalidate
          │                                    │
┌─────────┴─────────┐              ┌──────────┴──────────┐
│ usePerpsPosition  │              │   TradingService    │
│   ForAsset        │              │  (closePosition,    │
│                   │              │   placeOrder)       │
│ (30s cache +      │              │                     │
│  invalidation)    │              │   AccountService    │
└───────────────────┘              │  (withdraw)         │
                                   └─────────────────────┘

Order Redirect Flow (Long/Short buttons):
┌────────────────┐    navigate     ┌──────────────────┐   WS ready    ┌─────────────────┐
│  Token Details │ ──────────────→ │PerpsOrderRedirect│ ────────────→ │  Confirmation   │
│  (Long/Short)  │                 │  (Perps stack)   │  depositWith  │    Screen       │
│                │                 │  - init WebSocket │  Order()      │                 │
└────────────────┘                 │  - show loader    │               └─────────────────┘
                                   └──────────────────┘
                                          │ on error
                                          ▼
                                   goBack() + toast
```

## **Changelog**

CHANGELOG entry: Added lightweight position display and one-click
Long/Short trading on token details page for perps-enabled assets

## **Related issues**

Fixes:
[TAT-2427](https://consensyssoftware.atlassian.net/browse/TAT-2427)

## **Manual testing steps**

```gherkin
Feature: Perps position display on token details

  Scenario: User views token with open perps position
    Given user has an open perps position for ETH
    And user is on the wallet home screen

    When user navigates to ETH token details page
    Then user should see their open position displayed
    And the page should load quickly without full perps initialization

  Scenario: User taps on position to view details
    Given user is viewing token details with an open position displayed

    When user taps on the position card
    Then user should be navigated to the perps market detail page
    And the full perps environment should load

  Scenario: User with no perps position
    Given user does not have any perps positions

    When user navigates to any token details page
    Then no perps position card should be displayed
    And no perps API calls should be made (if user is not eligible)

  Scenario: Position display updates after closing position
    Given user has an open perps position for ETH
    And user is viewing the ETH token details page
    And user sees their open position displayed

    When user navigates to perps and closes their ETH position
    And user navigates back to the ETH token details page
    Then the position card should no longer be displayed
    And the cache should have been invalidated automatically

  Scenario: User taps Long button on token with perps market
    Given user is viewing ETH token details
    And ETH has a perps market available

    When user taps the "Long" button
    Then a loader screen should appear ("Preparing order...")
    And the WebSocket connection should initialize
    And depositWithOrder should be called
    And user should be navigated to the order confirmation screen

  Scenario: User taps Short button on token with perps market
    Given user is viewing ETH token details
    And ETH has a perps market available

    When user taps the "Short" button
    Then a loader screen should appear ("Preparing order...")
    And the WebSocket connection should initialize
    And depositWithOrder should be called
    And user should be navigated to the order confirmation screen

  Scenario: Long/Short buttons not shown for token without perps market
    Given user is viewing a token details page for a token without a perps market (e.g. USDC)

    Then no Long or Short buttons should be displayed in the action bar

  Scenario: Order redirect handles error gracefully
    Given user taps Long or Short on a perps-enabled token
    And the depositWithOrder call fails

    Then user should see an error toast notification
    And user should be navigated back to the token details page
```

## **Screenshots/Recordings**

### **Before**

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

### **After**

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

https://github.com/user-attachments/assets/245b905f-e9ab-4b65-a96b-49f81fe51afe

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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


[TAT-2427]:
https://consensyssoftware.atlassian.net/browse/TAT-2427?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Introduces new read-only provider/controller code paths and cache
invalidation wiring, plus a new navigation redirect that triggers
`depositWithOrder`; mistakes could lead to stale UI or incorrect
trade/confirmation routing, but changes are scoped and covered by new
tests.
> 
> **Overview**
> Adds a *read-only perps discovery path* so token details can query
perps `markets`/`positions`/`accountState` via lightweight HTTP (no full
controller/WebSocket init) by extending
`PerpsController`/`HyperLiquidProvider` with `readOnly` + `userAddress`
params and a standalone info client.
> 
> Introduces `PerpsCacheInvalidator` (and platform dependency plumbing)
and wires it into `TradingService` and `AccountService` to invalidate
read-only caches after successful state-changing actions, enabling hooks
like the new `usePerpsPositionForAsset` (30s TTL + invalidation
subscriptions) to stay fresh.
> 
> Updates token details (`AssetOverviewContent`) to display either a
compact `PerpsPositionCard` or a `PerpsDiscoveryBanner` based on the
read-only position lookup, adds geo-eligibility modal handling for
Long/Short, and implements a one-click trade flow via new
`PerpsOrderRedirect` route/screen that waits for perps connection, calls
`depositWithOrder`, then `StackActions.replace`s into redesigned
confirmations (or toasts + goes back on failure).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4a032ad. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: sahar-fehri <sahar.fehri@consensys.net>
<!--
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**

- Adds HyperEVM as selectable network for Bridge/Swap.
- Adds Bridge/Swap feature for HyperEVM (behind feature flags).
- Switches from `CHAIN_IDS` (transactions-controller) to
`NETWORK_CHAIN_ID` (internal, extends `CHAIN_IDS`) in bridge-related
files mapping [chain.id]: { data }. @reviewers happy to discuss about
that and where CHAIN_IDS could be located in the future. I used
`NETWORK_CHAIN_ID` because it already existed and was an extension of
the first one.

<!--
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: Adds Bridge and Swap feature to HyperEVM

## **Related issues**

Jira task: https://consensyssoftware.atlassian.net/browse/NEB-103

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

<img width="300" alt="image"
src="https://github.com/user-attachments/assets/4989e0aa-3f21-424e-9f84-0ddede78f698"
/>
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/e1ea34c3-7aca-4d24-b88b-030cfc230408"
/>
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/8310e5f8-8472-4fc8-b0f6-b532f4d04aef"
/>
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/09d2f5af-e791-4799-9927-64a86f2c89e1"
/>

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a new chain into bridge/swap routing via multiple constant maps
and supported-chain allowlists, so incorrect ids/addresses could break
swaps/bridges or mislabel activity. Dependency bumps to
bridge/transaction controllers add some integration risk.
> 
> **Overview**
> Enables Bridge/Swaps flows to recognize **HyperEVM** by adding its
chain id to supported network lists, display-name mappings, and
first‑party `Bridge`/`Swaps` contract address maps.
> 
> Updates bridge UI defaults by adding a HyperEVM default destination
token (USDC) and treating HyperEVM USDC as a stablecoin for default
slippage logic, while also standardizing several bridge constants to use
`NETWORK_CHAIN_ID` (from `customNetworks`) instead of `CHAIN_IDS`.
> 
> Bumps `@metamask/bridge-controller` and
`@metamask/transaction-controller` versions and adjusts unit tests/mocks
accordingly (including using a clearly invalid chain id in a
transaction-history test).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
225aa56. 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?
-->

### Root Causes

   **Flask Snap tests (Android + iOS):**
The invalid-entropy-source and past-date-scheduling error tests were
failing on **both** platforms but for different reasons:
- **iOS**: errors appear as native WebView alert dialogs —
`Assertions.expectTextDisplayed()` works.
- **Android**: errors render inside the web-view result span as JSON
with escaped quotes — `checkResultSpanIncludes()` is needed, matching a
quote-free substring to avoid `\"invalid\"` vs `"invalid"` mismatches.

The fix uses `device.getPlatform()` to pick the correct assertion per
platform.

   **Identity multi-SRP test:**
- `profile-sync-controller@27.1.0` bumped `snaps-controllers` from `^14`
to `^17.2.0`, slowing down the initial full sync enough that
`enqueueSingleGroupSync` silently drops subsequent sync requests while
`isAccountTreeSyncingInProgress` is still `true`.
- The "Discovering accounts..." spinner was still active when the test
tried to tap the Wallet 2 "Add account" button, which is disabled during
discovery — so the tap silently did nothing and the 4th account was
never created.

   ### Changes

**Flask Snap tests** (`test-snap-bip-32`, `test-snap-bip-44`,
`test-snap-get-entropy`, `test-snap-background-events`):
- Platform-conditional error assertions: native dialog check on iOS,
web-view result span check on Android
   - Increased assertion timeouts to 30s for slow CI emulators

   **Identity sync tests** (`multi-srp.spec.ts`):
- Wait for initial full sync via
`waitUntilSyncedAccountsNumberEquals(1)` before mutating accounts
- Wait for "Discovering accounts..." to disappear before tapping "Add
account" on Wallet 2
- Set up PUT event counter after initial sync completes to only track
subsequent mutations

   **Identity sync helpers** (`helpers.ts`):
   - Increased `BASE_TIMEOUT` from 12s to 30s
   - Improved error messages to include actual vs expected counts

## **Changelog**

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

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

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

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [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]
> **Low Risk**
> Test-only changes (timeouts, waits, and assertions) with no impact on
production logic; main risk is slightly longer CI runtime if failures
occur.
> 
> **Overview**
> Reduces flakiness in identity account-sync smoke coverage by **waiting
for the initial full sync to complete** before mutating accounts in
`multi-srp.spec.ts`, and by increasing the shared helper `BASE_TIMEOUT`
to 30s.
> 
> Improves debuggability of identity sync helpers by including *actual
vs expected* counts in timeout errors.
> 
> Stabilizes multiple Snap smoke tests by making error assertions
**platform-aware** (native alert on iOS vs web-view result on Android)
and by increasing assertion timeouts to 30s; also increases the optional
delay when tapping the V2 “Add Account” button (1.5s � 5s) to avoid UI
timing issues.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
92ac3cf. 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>
@pull pull Bot locked and limited conversation to collaborators Feb 9, 2026
@pull pull Bot added the ⤵️ pull label Feb 9, 2026
@pull pull Bot merged commit c6197b8 into Reality2byte:main Feb 9, 2026
2 of 36 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants