feat(perps-controller): centralize market category classification#9009
feat(perps-controller): centralize market category classification#9009abretonc7s wants to merge 6 commits into
Conversation
Add getMarketTypeFilter, isEquityAsset, and STOCK_LIKE_MARKET_TYPES so consumers share a single mapping from the MarketCategory data model to the UI MarketTypeFilter pills, instead of re-deriving the logic per client and drifting as new categories are added.
…tegory Reuse the existing matchesCategory category model instead of a parallel stock-like grouping. Move matchesCategory and applyMarketFilters from MarketDataService into marketUtils (pure helpers), add getMarketTypeFilter as the inverse of matchesCategory, and export all three. Drops the duplicate STOCK_LIKE_MARKET_TYPES / isEquityAsset added earlier.
- Collapse the stock-like categories into the 'stocks' bucket via a single isEquityAsset / STOCK_LIKE_MARKET_TYPES source. - Drive matchesCategory off an exhaustive MARKET_CATEGORY_TO_FILTER map instead of a per-category switch; keep only the genuine special cases (all, new, crypto's HIP-3 rule). - getMarketTypeFilter now returns the real user-facing bucket (crypto/stocks/commodities/forex/new) and never the 'all' sentinel. - Add MARKET_TYPE_FILTER named constants for the filter values.
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 1becc7b. Configure here.
getMarketTypeFilter treated a marketSource-only market (isHip3 unset, no marketType) as 'new', but matchesCategory still matched it as 'crypto'. Add a shared isHip3Market predicate (isHip3 OR marketSource) used by both, and treat an uncategorized HIP-3 market as 'new' in matchesCategory, so the two classify partial markets the same way. Equivalent for well-formed data.
aganglada
left a comment
There was a problem hiding this comment.
Don't we think we are exposing too many constants to the clients where we are better exposing methods that expose functionality (ie: filter markets). Constants can change names and structure. Methods expose a functionality that can change overtime, no matter what structure gets handled inside
| export const STOCK_LIKE_MARKET_TYPES: ReadonlySet<MarketType> = new Set([ | ||
| MarketCategory.Stock, | ||
| MarketCategory.PreIpo, | ||
| MarketCategory.Index, | ||
| MarketCategory.Etf, | ||
| ]); |
There was a problem hiding this comment.
we shouln't have have this anymore, we are showing all categories now
| export const isEquityAsset = (marketType?: string): boolean => | ||
| marketType !== undefined && | ||
| STOCK_LIKE_MARKET_TYPES.has(marketType as MarketType); |
There was a problem hiding this comment.
same we this, we shouldn't need this
| const MARKET_CATEGORY_TO_FILTER: Record<MarketCategory, MarketTypeFilter> = { | ||
| [MarketCategory.CryptoCurrency]: MARKET_TYPE_FILTER.Crypto, | ||
| [MarketCategory.Stock]: MARKET_TYPE_FILTER.Stocks, | ||
| [MarketCategory.PreIpo]: MARKET_TYPE_FILTER.PreIpo, | ||
| [MarketCategory.Index]: MARKET_TYPE_FILTER.Indices, | ||
| [MarketCategory.Etf]: MARKET_TYPE_FILTER.Etfs, | ||
| [MarketCategory.Commodity]: MARKET_TYPE_FILTER.Commodities, | ||
| [MarketCategory.Forex]: MARKET_TYPE_FILTER.Forex, | ||
| }; |
There was a problem hiding this comment.
we should align market category with filter category to be the same all singular. The current approach is a bit confusing

Explanation
MetaMask Mobile currently re-derives "which market category does this market belong to" in the client. Two in-flight features need the same mapping and each implemented it independently:
getMarketTypeFilterresolveMarketCategoryBoth lean on a mobile-local
isEquityAssetplus a hand-maintained stock/commodity/forex/crypto switch. Keeping this in the clients means every newMarketCategory(the controller already grewstock/pre-ipo/index/etfin 7.0.0) forces a parallel edit in each client, and the mappings drift.This PR centralises the classification in
@metamask/perps-controllerso consumers share one source of truth:STOCK_LIKE_MARKET_TYPES— the set of stock-like categories (stock,pre-ipo,index,etf) that share thestocksfilter and traditional market hours.isEquityAsset(marketType)— predicate over that set (moved out of the mobile client).getMarketTypeFilter(market)— maps aPerpsMarketDataonto a UIMarketTypeFilterpill. Stock-like →stocks;commodity→commodities;forex→forex; any HIP-3 signal (isHip3/isNewMarket/marketSource) on an otherwise-uncategorized market →all(the crypto pill only contains main-DEX markets); otherwise →crypto.Clients will replace their local copies with imports in follow-up PRs.
References
Checklist
Note
Medium Risk
Changes how markets map to UI category filters (HIP-3/
marketSourceandnewvscrypto), which can shift list filtering and shortcuts once clients adopt the exports; scope is additive with moved helpers rather than trading or auth paths.Overview
Centralizes perps market category classification in
@metamask/perps-controllerso mobile and other clients can import one model instead of duplicating switches.matchesCategoryandapplyMarketFiltersmove fromMarketDataServiceintomarketUtils; the service now importsapplyMarketFiltersonly. Both helpers are re-exported from the package root alongside new APIs:getMarketTypeFilter(UI bucket, collapsing stock/pre-ipo/index/etf intostocks),isEquityAsset,isHip3Market,STOCK_LIKE_MARKET_TYPES, andMARKET_TYPE_FILTERnamed constants.Classification logic is tightened:
isHip3MarkettreatsmarketSourceas HIP-3 whenisHip3is missing (route-param markets).matchesCategory/getMarketTypeFilterstay aligned on that signal;newmatches flagged new markets and uncategorized HIP-3 markets without amarketType. Granular filters (e.g.etfs) stay separate from the collapsedstocksbucket used bygetMarketTypeFilter.Adds unit tests for the new classification helpers in
marketUtils.test.tsand documents the release in the changelog.Reviewed by Cursor Bugbot for commit 998f88e. Bugbot is set up for automated code reviews on this repo. Configure here.