perf(perps): measure loading and reuse terminal trends - #34948
perf(perps): measure loading and reuse terminal trends#34948abretonc7s wants to merge 58 commits into
Conversation
|
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. |
93654e6 to
64597a0
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
🧪 Flaky unit test detectionRun history flaky detectionHistorical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow. Failures / runs sampled per window:
AI-detected flaky patterns
|
## **Description** Uses Perps Controller 12 global-market and atomic-user snapshots to make Homepage Perps data available earlier while preserving the direct-provider fallback. Requests are scoped to exact provider, network, DEX, and account identity. Account switches clear only user state and preserve account-independent markets and prices. Measured physical-device evidence: | Cohort | Current main | Candidate | Result | |---|---:|---:|---| | Android cold markets ready | 16.97s | 3.25s | 80.8% faster on Terminal-v2 hits; n=3/arm, hardware unmatched | | Android resolved Homepage Perps | 18.27s | 2.15s | 88.2% faster on Terminal-v2 hits | | iOS dev1 → empty dev2 | 2.42s | 2.63s | 3/3 correct first paints in both arms; no demonstrated regression | Terminal adoption was 3/4 before terminal-backend#49. Direct-provider fallback remains the correctness path and passed the post-rebase iOS smoke. Production instrumentation follows separately in MetaMask#34948. ## **Changelog** CHANGELOG entry: Improved Perps market and account loading on the wallet homepage ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3662 ## **Manual testing steps** ```gherkin Feature: Perps homepage bootstrap Scenario: switch from an account with a position to an empty account Given the wallet is unlocked on dev1 and Homepage Perps shows its position When the perps.performance recipe switches to dev2 Then no dev1 position is shown for dev2 And account-independent markets and prices remain available And dev2 resolves to an empty positions state Scenario: Terminal v2 is unavailable Given the wallet is unlocked and Terminal v2 returns an error When Homepage Perps loads Then the direct provider fallback supplies market data ``` Recipe: `perps.performance lifecycle=account_switch initial_account=dev1 account=dev2 initial_content_variant=positions content_variant=trending` Validation completed: - dev1 position → empty dev2: 3/3 correct candidate and 3/3 control. - Global markets remained populated during account-only switching. - Post-rebase iOS smoke: 38/38 checks passed. - Focused product suites and complete ConnectionManager suite passed. ## **Screenshots/Recordings** ### **Before** N/A — performance and state transitions are captured by the reusable recipe evidence. ### **After** N/A — performance and state transitions are captured by the reusable recipe evidence. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes Perps streaming, Terminal routing, and account-switch cache behavior where stale positions or mixed market sources would be user-visible; mitigated by identity checks, snapshot coalescing, and direct-provider fallback. > > **Overview** > **Faster homepage Perps bootstrap** by wiring Perps Controller **Terminal v2 global market snapshots** and a single **atomic user snapshot** (positions, orders, account) into `PerpsStreamManager`, with v1 market URLs kept for legacy enrichment. Mobile infra now passes `terminalApi.marketDataUrl` + `terminalApi.globalSnapshotUrl` (env-derived v2 host, dev-only `MM_PERPS_TERMINAL_GLOBAL_SNAPSHOT_URL` override). > > **Market data channel** treats Hyperliquid + configured v2 URL as a distinct source: can fetch **standalone** before WS readiness, only adopts controller cache when every row is `terminal-global-snapshot-mark`, and invalidates when provider/network/terminal vs global-snapshot mode changes so direct/Terminal data is not mixed. > > **User streams** coalesce on subscribe via `getUserDataSnapshot()` (Hyperliquid only), seed all three channels together, and ignore stale or in-flight snapshots after cache clear or newer live delivery. Channel caches/snapshots are **account-address scoped** so the wrong wallet’s data is not served. > > **Account switches** clear only user-scoped state in `PerpsConnectionManager` while keeping global markets/prices; live hooks (`usePerpsLivePositions`, orders, account) immediately enter loading and hide data when the selected address changes. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f069a8e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #34948 +/- ##
==========================================
+ Coverage 85.31% 85.36% +0.05%
==========================================
Files 6584 6598 +14
Lines 179351 180581 +1230
Branches 44527 44894 +367
==========================================
+ Hits 153014 154158 +1144
- Misses 16088 16110 +22
- Partials 10249 10313 +64 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…9939) ## Explanation Candle cleanup currently drops the promise returned by the HyperLiquid SDK unsubscribe call. A rejected cleanup therefore becomes an unhandled promise rejection during Mobile foreground recovery. This change routes resolved and pending cleanup through one idempotent helper. It consumes async rejections, catches synchronous throws, ignores the SDK's already-unsubscribed result, and logs unexpected failures. ## References - Related to MetaMask/metamask-mobile#34948 - Mobile uses a temporary Yarn patch until this fix is released. ## Validation - Focused HyperLiquidClientService suite: 90/90 passed - Changed-file ESLint and formatting passed - Perps controller changelog validation passed - The standalone package build requires prebuilt workspace dependency outputs in a fresh checkout; CI will run the complete build graph. ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation as appropriate - [x] I've communicated my changes to consumers by updating the package changelog - [ ] I've introduced breaking changes in this PR <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Defensive error handling on candle subscription teardown only; no trading, auth, or data-path changes. > > **Overview** > Stops HyperLiquid candle cleanup from dropping the SDK `unsubscribe()` promise, which could become an unhandled rejection (notably during Mobile foreground recovery). > > `subscribeToCandles` now routes resolved and pending teardown through one idempotent helper: it consumes async rejections, catches sync throws, ignores the SDK's "Already unsubscribed" result, and logs unexpected failures. Tests cover both the logged failure path and the idempotent late-cleanup path. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 377bfed. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c4b5fda. Configure here.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Performance Test Selection: |
|
|
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
✅ Passed Tests (1)
| Test | Platform | Device | Duration | Team | Recording |
|---|---|---|---|---|---|
| Perps add funds | Android | Google Pixel 8 Pro (v14.0) | 8.47s | @mm-perps-engineering-team | 📹 Watch |
Branch: perf/perps-loading-session-stack · Build: E2E · Commit: c73300b · View full run




Description
This PR improves Perps loading and measures the result across the Homepage and market detail. It now includes the former stacked PR #35146 because both parts depend on the same loading-session identity, stream provenance, context isolation, and Sentry contract.
The runtime changes are:
@metamask/perps-controller12.1.0 and reusemarket.trendon Homepage cards.Two shared APIs change in a backward-compatible way.
trace.tsaccepts an optional trace ID so asynchronous measurements reach the intended span.useSectionPerformanceaccepts optional metadata for existing Homepage TTC/DFD spans. Existing callers keep their current behavior.Request reduction
/infocandleSnapshotmainAll five visible sparklines remained rendered. A separate incomplete-snapshot run confirmed that only markets without a usable trend use candle fallback. This proves request elimination and fallback behavior. It does not claim a statistically stable end-to-end latency improvement.
Why validation took several rounds
The request reduction was straightforward. The harder part was making each timing represent the correct account, network, market, mode, and visible frame.
Device and review passes found cases that a normal happy path missed: global market data being reset on account change, old sockets completing a new generation, fallback sparklines freezing, stale chart and order-book data crossing a symbol switch, missing stats completing too early, and synchronous order-book failures never leaving loading.
The validation now checks contrarian paths before happy paths. Approval requires device-scoped raw records, HUD-visible screenshots, exact Mobile and Harness commits, no position or order mutation, and expected failures for missing, reordered, duplicated, or mixed-identity records. The canonical Recipe v1 work is tracked in experimental-metamask-harness #203.
The production dashboards mirror the documented lifecycle:
Changelog
CHANGELOG entry: Improved Perps loading by reusing Terminal market trends while retaining candle fallback when trends are unavailable
Related issues
Refs: https://consensyssoftware.atlassian.net/browse/TAT-3662
Manual testing steps
Final Android validation
Physical Pixel 6
21261FDF6001SP, Mobilee52f8e15cf83971120c0359b50f406d0d25f8b00, Harness production code8783fac90920153d1732769d41dd48638fdc9c65. Harness PR headf9fc523574cacaa346a91f9af1c71a0500ea513eadds only the corrected continuity-test expectation.The serialized Recipe v1 runs passed with
--heal off:perps.performancenetwork-switch flow: 41/41Every run records
dirty: false, valid provenance with no drift,recovered: [], andmutations: []. All ten screenshots have a current-run HUD and settled content.The account-switch detail session now records replacement data after reconnect: account at 5762 ms, price at 5771 ms, and empty positions/orders at 6168 ms. The Homepage sample records one coherent Terminal v2 sequence with TTC 3834 ms and DFD 4174 ms. These are single device samples used to validate measurement semantics, not a latency benchmark.
The Harness implementation and reproducible graph are in experimental-metamask-harness #203.
Screenshots/Recordings
Before
The UI is intentionally unchanged. Before evidence is the redacted request summary with ten
candleSnapshotcalls.After
Recipe screenshots confirm the expected Homepage, Lite, and Pro states with the HUD visible. The complete-trend request summary contains zero

candleSnapshotcalls.Pre-merge author checklist
Performance checks
Pre-merge reviewer checklist
Note
Medium Risk
Changes affect live Perps streams, chart/order-book lifecycle, and Sentry trace correlation across account/network switches; incorrect generation handling could mis-report readiness or briefly show stale market data, but scope is UI and instrumentation rather than order execution.
Overview
This PR tightens Perps loading measurement and market-detail correctness when users switch markets, modes, accounts, or network context. Lite and Pro detail screens now drive section-level readiness (market, price, chart, stats, insights, account, order book, positions/orders) through
usePerpsMarketDetailSession, reset CUF traces on generation changes, and gatePerpsMarketDetailLiveon current-symbol readiness instead of a single coarse hydration check.Market context isolation is the main runtime change: charts, candles, order books, focused price, and fullscreen/OHLC UI wait on
usePerpsMarketContextand aresetKey, drop stale cross-symbol data, and reportonResolvedfrom Advanced Chart and Pro sub-panels. The market-details router forwardsgenerationTrigger(initial,market_switch,mode_switch) from navigation params; the header market picker setsdetailGenerationTrigger: 'market_switch'.useMarketInsightsignores out-of-order fetches after symbol changes.Observability: mobile infrastructure correlates preload traces with the active loading session, routes child measurements to the correct trace id, and records controller construction time. A yarn patch on
@metamask/perps-controlleradds safer MYX registration, bounded HyperLiquidallMidsdebug logging, and guarded candle WebSocket unsubscribe..watchmanconfigignorestemp.Reviewed by Cursor Bugbot for commit 1613a56. Bugbot is set up for automated code reviews on this repo. Configure here.