MetaMask / MetaMask/metamask-mobile

Spike: Performance Audit for Perps

Open
#23,696 0 comments 0 reactions 1 assignee Claimed by @gambinish View on GitHub
Dominant language
TypeScript
Stars
3k
Forks
1.7k
Avg merge
1d 14h
Merged PRs (30d)
669

Description

### What is this about?

This is a performance audit spike for the Perps feature, identifying optimization opportunities across three key areas:

- **API audit**: Duplicate geolocation calls, redundant HyperLiquid API requests
- **Controller state analysis**: Dead Redux state fields, selector memoization issues
- **UI rendering performance**: MarketList triple-rendering, Activity tab unbounded transactions

The audit identifies 7 actionable improvements that will reduce API calls by ~50%, eliminate unnecessary re-renders, and improve perceived performance.

### Scenario

**User flows audited:**

1. App cold start → Excessive geolocation API calls (4x instead of 1x)
2. Opening Perps Home Page → ~25+ HyperLiquid API calls (should be ~12)
3. Opening Market Details → 7 API calls (should be 3)
4. Opening Activity tab, switching between other Activity tabs → Unbounded transaction list causes slow rendering
5. App backgrounded >60s then foregrounded → Stale WebSocket connections

### Design

N/A - No design changes required except for MarketList tab removal (requires designer input for filter button style).

### Technical Details

#### 1. Cap Perps Activity Tab Transaction Count

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2212

**Problem**: No limit on transactions loaded into UI causes slow rendering.

**Solution**: Add `TRANSACTIONS_HISTORY_LIMIT: 100` constant and apply `.slice()` after deduplication in `usePerpsTransactionHistory.ts`.

**Files**:

- `app/components/UI/Perps/constants/transactionsHistoryConfig.ts`
- `app/components/UI/Perps/hooks/usePerpsTransactionHistory.ts`

---

#### 2. Selector Memoization Issues in PerpsMarketBalanceActions

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2213

**Problem**: Inline selector creates new array reference every render, triggering Redux warning and unnecessary re-renders.

**Solution**: Create memoized selector using `createSelector` that filters withdrawal requests by account address.

**Files**:

- `app/components/UI/Perps/screens/HomeView/PerpsMarketBalanceActions.tsx`

---

#### 3. Fix WebSocket Connection Stale After Background (more investigation needed)

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2214

**Problem**: HyperLiquid closes WebSocket after 60s inactivity, but app thinks it's still connected when foregrounded.

Investigate `throttle` configuration for websocket connection. It can appear like a lag. It is meant to optimize the client side optimization (optionally). Does it make sense to connect the socket in context? In reality it behaves similar to context, and can cause the same problems.

**Solution**:

- Fix `keepAlive` config property name (should be `keepAliveInterval`)
- Add `validateAndReconnectIfNeeded()` method with ping health check
- Track background duration and validate connection on foreground

**Files**:

- `app/components/UI/Perps/types/config.ts`
- `app/components/UI/Perps/constants/hyperLiquidConfig.ts`
- `app/components/UI/Perps/constants/perpsConfig.ts`
- `app/components/UI/Perps/services/PerpsConnectionManager.ts`
- `app/components/UI/Perps/hooks/usePerpsConnectionLifecycle.ts`
- `app/components/UI/Perps/providers/PerpsConnectionProvider.tsx`

---

#### 4. Coalesce Geolocation API Calls

Jira Issue: https://consensyssoftware.atlassian.net/browse/TAT-2210

**Problem**: Same geolocation endpoint called by 4 separate features (Ramp, Perps, Rewards, Card) with no shared caching.

**API Call Evidence**:

```
https://on-ramp.dev-api.cx.metamask.io/geolocation - 1858ms
https://on-ramp.dev-api.cx.metamask.io/geolocation - 3127ms
https://on-ramp.uat-api.cx.metamask.io/geolocation - 1856ms
https://polymarket.com/api/geoblock - 1807ms
```

**Solution**: Create centralized `GeolocationController` with TTL caching and promise deduplication.

**Files**:

- Create: `app/core/Engine/controllers/geolocation-controller/GeolocationController.ts`
- Modify: `app/components/UI/Perps/controllers/services/EligibilityService.ts`
- Modify: `app/components/UI/Ramp/hooks/useDetectGeolocation.ts`
- Modify: `app/core/Engine/controllers/rewards-controller/services/rewards-data-service.ts`
- Modify: `app/components/UI/Card/sdk/CardSDK.ts`

---

#### 5. Remove Dead PerpsController State Fields

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2215

**Problem**: `connectionStatus` and `positions` fields never properly used - live data flows through React Context instead of Redux.

**Solution**: Remove both fields from type, defaults, and metadata.

**Files**:

- `app/components/UI/Perps/controllers/PerpsController.ts`
- `app/components/UI/Perps/controllers/PerpsController.test.ts`
- `app/components/UI/Perps/__mocks__/serviceMocks.ts`

---

#### 6. Optimize Perps API Calls

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2211

**Problem**: ~25+ API calls on Home Page load with significant duplication.

| API Call | Before | After |
| ------------------ | ------ | ----- |
| `perpDexs` | 2 | 1 |
| `meta` per DEX | 2+ | 1 |
| `metaAndAssetCtxs` | 6 | 5 |
| `allMids` | 5 | 5 |
| **Total** | ~25+ | ~12 |

**Solution**:

- Share meta cache between Provider and SubscriptionService
- Remove redundant `meta()` call in `getMarketDataWithPrices()` (extract from `metaAndAssetCtxs`)
- Consolidate `perpDexs` caching

**Files**:

- `app/components/UI/Perps/services/HyperLiquidSubscriptionService.ts`
- `app/components/UI/Perps/controllers/providers/HyperLiquidProvider.ts`
- `app/components/UI/Perps/services/HyperLiquidClientService.ts` (optional: request deduplication)

---

#### 7. MarketList Performance: Replace Swipeable Tabs with Filter Buttons

Jira issue: https://consensyssoftware.atlassian.net/browse/TAT-2216

**Problem**: Swipeable tabs create 3 simultaneous `PerpsMarketList` instances with complex scroll synchronization.

| Metric | Before | After |
| --------------------- | ------------------ | ----- |
| FlashList instances | 3 | 1 |
| Scroll event handlers | 2 | 0 |
| Animation overhead | Fade + scroll sync | None |

**Solution**: Replace `TabsBar` + swipeable `ScrollView` with `ButtonFilter` pills + single `PerpsMarketList`.

**Files**:

- Create: `app/components/UI/Perps/Views/PerpsMarketListView/components/PerpsMarketCategoryFilter/`
- Modify: `app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketFiltersBar.tsx`
- Modify: `app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx`
- Modify: `app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.styles.ts`

**Functions**:
- `transformOrdersToTransactions`
- `transformFillsToTransactions`
- `transformFundingToTransactions`

**Design Required**: Filter button style, responsive behavior, layout spacing

### Threat Modeling Framework

N/A - Performance optimizations only, no security-sensitive changes.

### Acceptance Criteria

- [ ] Activity tab renders in <500ms with 100+ transactions
- [ ] No Redux selector warnings in console
- [ ] WebSocket reconnects successfully after 60s+ background
- [ ] Geolocation API called maximum 1x per 5-minute window
- [ ] PerpsController state does not contain `connectionStatus` or `positions` fields
- [ ] Home Page makes ≤12 HyperLiquid API calls (down from ~25)
- [ ] Market Details makes ≤3 API calls (down from 7)
- [ ] MarketList renders single FlashList instance (down from 3)
- [ ] Implementation tickets created for all 7 findings

### Stakeholder review needed before the work gets merged

- [x] Engineering (needed in most cases)
- [x] Design (for MarketList filter button style)
- [ ] Product
- [ ] QA (automation tests are required to pass before merging PRs but not all changes are covered by automation tests - please review if QA is needed beyond automation tests)
- [ ] Security
- [ ] Legal
- [ ] Marketing
- [ ] Management (please specify)
- [ ] Other (please specify)

### References

- `docs/readme/release-build-profiler.md` - Profiling guide
- `docs/readme/performance.md` - Optimization patterns
- [Nightly RC with builds release profiler enabled](https://consensys.slack.com/archives/C093JQSEPJL)
- Local debug builds
- [API tracing audit training](https://www.loom.com/share/87f745c6635843e6b332886cb175407b?sid=6e936653-09f9-44a5-9eff-d9e0b6d2cbf4)
- Tools: API tracing via Reactotron, SpeedScope, Redux DevTools

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.