feat(perps): add lightweight position display on token details page#25685
Conversation
Add usePerpsPositionForAsset hook that fetches position data via HTTP-only API call with 30s client-side cache, avoiding full PerpsController initialization. The full perps environment loads only when user taps the position to view market details. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
Add PerpsCacheInvalidator service to ensure cached position data stays fresh when users perform actions in perps environment. - Create PerpsCacheInvalidator singleton service with subscribe/invalidate API - Hook usePerpsPositionForAsset subscribes to positions and accountState events - TradingService calls invalidate on placeOrder and closePosition success - AccountService calls invalidate on withdraw success - Add comprehensive unit tests (23 for service, 6 for hook integration) - Document pattern in perps-architecture.md This ensures token details page shows accurate position status even after user closes positions in perps, while maintaining 30s cache performance. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use activeProviderInstance when available for readOnly mode queries, with HyperLiquidProvider fallback for pre-initialization discovery. This respects the provider abstraction while maintaining functionality. - Extract getReadOnlyClearinghouseState helper in HyperLiquidProvider - Add loading state handling in AssetOverviewContent - Update test descriptions to match new behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add PerpsCacheInvalidator.invalidate() calls for operations that modify position data but were missing cache invalidation: - updateMargin: margin changes affect position leverage/liquidation price - flipPosition: bypasses placeOrder, needs explicit invalidation - closePositions (batch): native batch close path was missing invalidation This ensures usePerpsPositionForAsset hook shows fresh data after these operations instead of waiting for 30-second cache expiry. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace direct import of PerpsCacheInvalidator singleton in controller services with dependency injection via PerpsPlatformDependencies. This maintains loose coupling and allows the controller/services to be extracted to core without mobile-specific dependencies. Changes: - Add PerpsCacheInvalidator interface to PerpsPlatformDependencies - Add InvalidateCacheParams type for method parameters - Implement adapter in mobileInfrastructure.ts - Update TradingService and AccountService to use this.deps.cacheInvalidator - Update test mocks to include cacheInvalidator Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
c9da792 to
4d8d087
Compare
…ositions # Conflicts: # app/components/UI/AssetOverview/TokenOverview.testIds.ts # app/components/UI/Perps/controllers/PerpsController.test.ts # app/components/UI/TokenDetails/components/AssetOverviewContent.tsx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## **Description**
Adds geolocalisation check on long and short buttons
## **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 geolocalisation check on long/short buttons on
token details page
## **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/99417619-cd37-41a1-bfa4-df16f6c8f3ca
## **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**
> Touches token-details primary action flow and navigation locking;
regressions could block Long/Short actions or leave buttons locked, but
changes are localized and covered by unit tests.
>
> **Overview**
> Adds a perps geolocation/eligibility gate to Token Details
**Long/Short** actions: ineligible users now see a
`PerpsBottomSheetTooltip` geo-block modal and an analytics event
(`MetaMetricsEvents.PERPS_SCREEN_VIEWED`) is tracked instead of
navigating/starting the perps flow.
>
> Updates `TokenDetailsActions` to expose a parent-controlled reset for
its navigation lock so the UI can be unlocked after a non-navigating
modal is dismissed, and adds unit tests validating eligible vs
ineligible behavior.
>
> Adjusts `PerpsOrderRedirect` loader presentation to match the perps
loading skeleton layout (wrap in `Box`, use `fullScreen={false}`) and
updates its test accordingly.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6d912e2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Core Controller Changes (CRITICAL):
New Components/Services:
UI Changes:
Navigation:
Tag Selection Rationale:
The changes are well-tested with unit tests and follow established patterns (readOnly mode, cache invalidation). Risk is medium because while the controller changes are significant, they add new functionality without modifying existing critical paths. Performance Test Selection: |
| readOnly: true, | ||
| userAddress, | ||
| }), | ||
| ]); |
There was a problem hiding this comment.
Duplicate API calls for same clearinghouse state data
Medium Severity
The usePerpsPositionForAsset hook calls getPositions and getAccountState in parallel via Promise.all, but both readOnly paths internally call getReadOnlyClearinghouseState(userAddress), which hits the same clearinghouseState HTTP endpoint with identical parameters. The single response already contains both assetPositions (for positions) and marginSummary/withdrawable (for account state). This doubles HTTP requests on every cache miss or invalidation for each perps-enabled token details page.
Additional Locations (1)
There was a problem hiding this comment.
I can follow up with a separate PR for this 🙏
|





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
readOnlymode pattern that allows querying perps data (positions, account state, markets) via lightweight HTTP API calls without WebSocket, wallet, or full controller initialization. Combined with aPerpsCacheInvalidatorservice that ensures cached data is refreshed when positions change. Added Long/Short buttons on token details that navigate through a newPerpsOrderRedirectscreen 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 eventsReadOnly Mode (Controller/Provider):
getPositions({ readOnly: true, userAddress })- Query positions without full initgetAccountState({ readOnly: true, userAddress })- Query account state without full initcreateStandaloneInfoClientfor lightweight HTTP-only queriesCache Invalidation Service:
PerpsCacheInvalidator- Singleton service for loosely-coupled cache invalidationpositions,accountState)invalidate()after successful operationsOne-Click Trade from Token Details:
PerpsOrderRedirectscreen - a redirect/loader that initializes the WebSocket connection inside the Perps stack, callsdepositWithOrder(), then navigates to the confirmation screenusePerpsActionshook - provideshandlePerpsAction(direction)callback for Long/Short buttons, navigates toPerpsOrderRedirectwith direction and asset paramsAssetOverviewContentintegrates Long/Short buttons viausePerpsActionswhen a perps market exists for the tokenDynamic Button Layout:
Documentation:
docs/perps/perps-architecture.mdArchitecture
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
Manual testing steps
Screenshots/Recordings
Before
After
Simulator.Screen.Recording.-.iPhone16Pro-Delta.-.2026-02-06.at.15.22.47.mp4
Pre-merge author checklist
Pre-merge reviewer checklist
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/accountStatevia lightweight HTTP (no full controller/WebSocket init) by extendingPerpsController/HyperLiquidProviderwithreadOnly+userAddressparams and a standalone info client.Introduces
PerpsCacheInvalidator(and platform dependency plumbing) and wires it intoTradingServiceandAccountServiceto invalidate read-only caches after successful state-changing actions, enabling hooks like the newusePerpsPositionForAsset(30s TTL + invalidation subscriptions) to stay fresh.Updates token details (
AssetOverviewContent) to display either a compactPerpsPositionCardor aPerpsDiscoveryBannerbased on the read-only position lookup, adds geo-eligibility modal handling for Long/Short, and implements a one-click trade flow via newPerpsOrderRedirectroute/screen that waits for perps connection, callsdepositWithOrder, thenStackActions.replaces into redesigned confirmations (or toasts + goes back on failure).Written by Cursor Bugbot for commit 4a032ad. This will update automatically on new commits. Configure here.