feat(perps-controller): sync controller from mobile 394fe407d2#8560
Merged
abretonc7s merged 3 commits intomainfrom Apr 23, 2026
Merged
feat(perps-controller): sync controller from mobile 394fe407d2#8560abretonc7s merged 3 commits intomainfrom
abretonc7s merged 3 commits intomainfrom
Conversation
Syncs app/controllers/perps/ from mobile commit 394fe407d2. Changes: - HL Unified-mode live balance: spotState WS + tradeable-balance + total-balance math ([#29226](MetaMask/metamask-mobile#29226)) - Complete spot-balance parity with extension ([#29110](MetaMask/metamask-mobile#29110)) - Preserve integer trailing zeros when szDecimals=0 ([#29016](MetaMask/metamask-mobile#29016)) - Add coalescePerpsRestRequest util + accountUtils; account-scoped REST cache and guarded cache writes - Preserve candle pagination cancellation; skip coalesce for explicit-endTime candle paging - Parity with extension rate-limit fix + provider-agnostic forceRefresh - Pin resolved account id to forwarded provider params; defer account resolution to non-paginated cache path - Force-refresh activity mount and evict expired coalesce entries - Regenerate PerpsController method action types; shrink rate-limit diff and drop verbose history logs
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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 7997c64. Configure here.
10 tasks
Syncs mobile fixes for the two bugbot comments on this PR: - hyperLiquidAdapter: drop the dead spotState branch and optional parameter from adaptAccountStateFromSDK. Every caller was already passing a single argument; the spot branch summed balances across ALL coins, which would have silently diverged from the USDC-only policy enforced by addSpotBalanceToAccountState via SPOT_COLLATERAL_COINS if a future caller had wired it in. - HyperLiquidSubscriptionService: normalize event.user.toLowerCase() before writing #cachedSpotStateUserAddress so the strict-equal check in #ensureSpotState hits the cache regardless of whether HyperLiquid returns a checksummed or lowercase address on the WS feed. Previously the mismatch triggered a redundant REST spotClearinghouseState refetch per subscribeToAccount call. Mobile source: MetaMask/metamask-mobile#29243
aganglada
approved these changes
Apr 23, 2026
Merged
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Apr 23, 2026
## **Description** Addresses the two bugbot findings raised on the corresponding core sync PR ([MetaMask/core#8560](MetaMask/core#8560)). Both issues are sourced from mobile — fix here first, then re-sync to core. ### 1. Dead spot branch in `adaptAccountStateFromSDK` (Medium) `app/controllers/perps/utils/hyperLiquidAdapter.ts` — the exported function still advertised an optional `spotState` param and ran a spot branch that sums balances across **all** spot coins. Every current caller passes a single argument only; the live path layers spot balances afterwards via `addSpotBalanceToAccountState`, which restricts spot math to **USDC only** (`SPOT_COLLATERAL_COINS`). The dormant branch is a future-caller trap: anyone wiring `spotState` in would silently get ALL-coins behavior, diverging from the USDC-only policy enforced elsewhere. Dropped the param, the branch, and the now-orphaned import. ### 2. Spot-state address casing causes redundant REST refetches (Low) `app/controllers/perps/services/HyperLiquidSubscriptionService.ts` line 1147 — the spot-state WS callback filters `event.user` with `.toLowerCase()` but then stores the raw `event.user` in `#cachedSpotStateUserAddress`. The strict-equal check in `#ensureSpotState` (line 1032) compares that cached value against the wallet's lowercase address, so when HyperLiquid returns a checksummed address on the WS feed the cache check always misses and a redundant REST `spotClearinghouseState` fetch is triggered per `subscribeToAccount` call (it self-heals once REST rewrites the cache, but at a per-call cost until convergence). Normalize at the store site to match the REST path's lowercase convention. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [TAT-3066](https://consensyssoftware.atlassian.net/browse/TAT-3066) Related: [MetaMask/core#8560](MetaMask/core#8560) — re-sync to core once this lands. ## **Manual testing steps** ```gherkin Feature: Perps account balance aggregation (unchanged behavior) Scenario: Unified-mode user opens perps with spot balance Given the user is on the Perps Home screen with an open position Then availableToTradeBalance, totalBalance, marginUsed render as before the change And the USDC-only spot policy continues to be applied by addSpotBalanceToAccountState Scenario: WS spotState event arrives with a checksummed address Given subscribeToAccount is active When HyperLiquid pushes a spotState WS event with a checksummed `event.user` Then subsequent #ensureSpotState calls skip the REST refetch (cache hit) And metro.log shows no redundant `refreshSpotState` fetches between account updates ``` ## **Screenshots/Recordings** No visual change. Fix is in controller/service layer; account balance values render identically. ### **Before** N/A — code-level behavioral fixes, no UI surface. ### **After** N/A — code-level behavioral fixes, no UI surface. ## **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 - Obsolete spot-branch tests removed alongside the dead code. The remaining 61 tests in `hyperLiquidAdapter.test.ts` still pass. - [x] I've documented my code using JSDoc format if applicable - Added a short comment above `adaptAccountStateFromSDK` explaining why spot logic is intentionally excluded. - [ ] I've applied the right labels on the PR #### Performance checks (if applicable) - [ ] I've tested on Android - [ ] I've tested with a power user scenario - [ ] I've instrumented key operations with Sentry traces for production performance metrics - Fix is perf-positive (removes a redundant REST fetch) and does not introduce new Sentry surfaces. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR - [ ] 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-3066]: https://consensyssoftware.atlassian.net/browse/TAT-3066?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Updates core perps account-balance adaptation and spot-state caching logic; mistakes could surface as incorrect balances or unnecessary network calls, but changes are small and covered by existing call patterns/tests. > > **Overview** > Fixes two perps core-sync issues. > > `adaptAccountStateFromSDK` is now **perps-only**: it drops the optional `spotState` parameter and removes the dormant spot-balance summation branch, ensuring spot math is applied exclusively via `addSpotBalanceToAccountState` (USDC-only policy) and simplifying `availableToTradeBalance`/`totalBalance` to perps values. > > Spot-state WebSocket handling now lowercases `event.user` when caching `#cachedSpotStateUserAddress`, preventing cache misses that previously triggered redundant REST `spotClearinghouseState` refetches when HL returned checksummed addresses. Related unit tests that covered the removed spot branch were deleted. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d4c6805. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Apr 23, 2026
## Explanation This release bumps \`@metamask/perps-controller\` from \`3.2.0\` to \`3.3.0\`. ### Changes included **\`@metamask/perps-controller\` v3.3.0** ([MetaMask#8560](MetaMask#8560)) **Added** - Add \`coalescePerpsRestRequest\` utility for deduplicating concurrent REST requests with account-scoped cache keys - Add \`accountUtils\` helpers for resolving the active perps account id and pinning it to forwarded provider params **Changed** - Account-scope the REST cache and guard cache writes so mount load stays cacheable without cross-account bleed - Make \`forceRefresh\` provider-agnostic and align rate-limit handling with the extension - Regenerate \`PerpsController\` method action types; shrink rate-limit diff and drop verbose history logs **Removed** - Drop the dead \`spotState\` parameter from \`adaptAccountStateFromSDK\` — spot balances are layered on by \`addSpotBalanceToAccountState\`, which enforces the USDC-only policy via \`SPOT_COLLATERAL_COINS\`, removing the dormant branch keeps one source of truth and prevents a future caller from silently getting ALL-coins behavior **Fixed** - HyperLiquid Unified-mode live balance: subscribe to \`spotState\` WS and compute tradeable/total balance from on-chain math - Complete spot-balance parity with the extension consumer - Preserve integer trailing zeros when \`szDecimals=0\` in \`perpsFormatters\` - Preserve candle pagination cancellation and skip coalesce for explicit-\`endTime\` candle paging to avoid stale pages - Defer account resolution on the non-paginated cache path to prevent race conditions - Force-refresh on activity mount and evict expired coalesce entries so stale promises cannot resolve to cache - Normalize \`event.user\` to lowercase when caching the spot-state WS address so \`#ensureSpotState\` hits the cache instead of triggering a redundant REST \`spotClearinghouseState\` refetch when HyperLiquid returns a checksummed address ## References - [MetaMask#8560](MetaMask#8560) ## Checklist - I've updated the test suite for new or updated code as appropriate - I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - I've communicated my changes to consumers by updating changelogs for packages I've changed - I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Syncs app/controllers/perps/ from mobile commit 394fe407d2.
Changes:
coalescePerpsRestRequestutil +accountUtils; account-scoped REST cache and guarded cache writesforceRefreshPerpsControllermethod action types; shrink rate-limit diff and drop verbose history logsNote
Medium Risk
Touches core market-data fetching, caching, and account-balance computation/WS subscriptions; regressions could surface stale/cross-account activity data or incorrect balances, but changes are localized and add explicit cache scoping and refresh escape hatches.
Overview
Adds a new service-layer REST request coalescing/TTL cache (
coalescePerpsRestRequest) and wires it intoMarketDataService(orders/fills/funding) and HyperLiquid candle snapshots to reduce duplicate REST traffic and 429s, with an end-to-endforceRefreshbypass for user-initiated refreshes.Fixes HyperLiquid unified-mode balance parity by subscribing to
spotStateover WS, computing spot-adjustedtotalBalanceplus a newavailableToTradeBalance, and centralizing spot math inaccountUtils(including USDC-only spot collateral policy). Also scopes caches by resolved account via newPerpsProvider.getCurrentAccountId()and updates controller/provider method signatures and adapter/formatter behavior (e.g., preserve integer trailing zeros whenszDecimals=0).Reviewed by Cursor Bugbot for commit 1660541. Bugbot is set up for automated code reviews on this repo. Configure here.