CP-14879: hide Hyperliquid networks (HyperEVM/HyperCore) behind hyperliquid-support feature flag - #4005
Conversation
There was a problem hiding this comment.
Pull request overview
Introduces a PostHog feature gate to hide Hyperliquid networks (HyperEVM/HyperCore) from Core Mobile until explicit support is shipped, preventing these chains from appearing/auto-enabling based on backend network responses.
Changes:
- Added
hyperliquid-supportfeature gate (default OFF) and Redux selector to block/allow Hyperliquid visibility. - Filtered Hyperliquid networks out of
NetworkService.getNetworksand threaded the new flag through React Query keys + cache accessors so toggling refetches. - Updated Manage Networks UI to exclude Hyperliquid from “Avalanche L1s” and show a dedicated “Hyperliquid” section when enabled; added unit tests for filtering + helper utilities.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core-mobile/app/utils/network/isHyperliquidNetwork.ts | Adds Hyperliquid identification helpers/constants. |
| packages/core-mobile/app/utils/network/isHyperliquidNetwork.test.ts | Adds unit tests for Hyperliquid detection helpers. |
| packages/core-mobile/app/store/posthog/types.ts | Registers default OFF value for the new Hyperliquid feature gate. |
| packages/core-mobile/app/store/posthog/slice.ts | Adds selector to determine whether Hyperliquid support is blocked. |
| packages/core-mobile/app/store/network/slice.ts | Threads Hyperliquid gating into cached-networks selectors. |
| packages/core-mobile/app/store/network/slice.test.ts | Updates mocks to include the new PostHog selector. |
| packages/core-mobile/app/services/posthog/types.ts | Adds FeatureGates.HYPERLIQUID_SUPPORT enum value. |
| packages/core-mobile/app/services/network/NetworkService.ts | Filters Hyperliquid networks from fetched networks unless enabled. |
| packages/core-mobile/app/services/network/NetworkService.test.ts | Adds tests verifying Hyperliquid include/exclude behavior. |
| packages/core-mobile/app/new/features/accountSettings/screens/manageNetworks/ManageNetworksScreen.tsx | Moves Hyperliquid chains out of “Avalanche L1s” and into a new “Hyperliquid” section when present. |
| packages/core-mobile/app/new/common/containers/BalanceManager.tsx | Updates networks prefetch query key + params to include Hyperliquid gating. |
| packages/core-mobile/app/hooks/networks/utils/getNetworksFromCache.ts | Extends cache lookup key to include Hyperliquid gating. |
| packages/core-mobile/app/hooks/networks/useNetworks.ts | Threads Hyperliquid gating into the networks query hook. |
| packages/core-mobile/app/hooks/networks/useGetNetworks.ts | Updates networks query key and service call to include Hyperliquid gating. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const getNetworksFromCache = ({ | ||
| includeSolana = false | ||
| includeSolana = false, | ||
| includeHyperliquid = false | ||
| }: { | ||
| includeSolana: boolean | ||
| includeHyperliquid: boolean | ||
| }): Networks | undefined => { | ||
| return queryClient.getQueryCache().find({ | ||
| queryKey: [ReactQueryKeys.NETWORKS, includeSolana] | ||
| queryKey: [ReactQueryKeys.NETWORKS, includeSolana, includeHyperliquid] | ||
| })?.state.data as Networks | undefined | ||
| } |
There was a problem hiding this comment.
Done in d1c2acf — switched to queryClient.getQueryData with the exact key and dropped the dead defaults (both call sites pass explicitly, so params stay required).
| export function isHyperliquidNetwork(network?: Network): boolean { | ||
| if (!network) { | ||
| return false | ||
| } | ||
|
|
||
| return ( | ||
| isHyperliquidChainId(network.chainId) || | ||
| network.chainName === HYPEREVM_CHAIN_NAME || | ||
| network.chainName === HYPERCORE_CHAIN_NAME | ||
| ) | ||
| } |
There was a problem hiding this comment.
Done in d1c2acf — chain-name fallback now trims and compares case-insensitively (still exact token match, so no false-positive risk).
Coverage report ✅2/2 packages passed thresholds 🟢 @avalabs/core-mobile
🟢 @avalabs/k2-alpine
Artifacts and threshold sources
Source run: Mobile PR |
ruijia1in
left a comment
There was a problem hiding this comment.
Adversarial review (self-review pass)
Red-teamed the full diff plus both cache read paths (redux selectors + the react-query hook), the enabled-networks/balance derivation, and how includeSolana differs from the new filter.
No critical/high issues — flag defaults OFF, query keys line up across all four cache readers, and a stale enabled HyperEVM id resolves to undefined and drops out of selectEnabledNetworks with no crash.
Main takeaway (design)
includeHyperliquid is modeled as a fetch/cache parameter, but unlike includeSolana (a real backend param: /networks?includeSolana) it's a pure client-side filter applied after the response. Putting it in the query key:
- triggers a full
/networks+ deBank refetch on flag flip even though the request is byte-for-byte identical, and - fragments the cache into two entries that differ only by a post-filter, and
- forces the boolean to thread through ~6 files, growing combinatorially with every future gated network.
Cleaner / more future-proof: filter at the read layer instead. Leave getNetworks/useGetNetworks/getNetworksFromCache/BalanceManager and the query key keyed only on includeSolana, and apply one shared excludeHyperliquid(networks) predicate at the two spots that already read the PostHog flag — useNetworks and selectNetworks/selectAllNetworks. This still fixes the auto-enable/balances bug (verified: selectEnabledNetworks looks up networks[chainId], so stripping the map drops the stale id), avoids the refetch + cache fragmentation, and makes the next gated network a one-line predicate instead of new plumbing. Inline notes below.
Reviewed clean
- Security: no secrets/PII/logging, no injection surface, no new deps.
- Mutation-during-iteration:
Object.entries()snapshots keys, so the in-placedeleteis safe. getQueryDataswap (from.find()): more correct now that the key has 3 segments; all callers pass all 3.- React memo deps in
ManageNetworksScreenare correct; the Hyperliquid section is gated implicitly via an emptynetworksmap when the flag is off.
4b0b660 to
2debcaa
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
packages/core-mobile/app/hooks/networks/useGetNetworks.ts:26
- PR description says the NETWORKS React Query key is now
[NETWORKS, includeSolana, includeHyperliquid]and that flippinghyperliquid-supporttriggers a refetch. The implementation here explicitly keepsincludeHyperliquidout of the query key and relies on a read-timeselectfilter instead, so toggling the flag will not refetch and will instead re-derive from cached data. Please align the PR description with the actual behavior (or adjust the query key if refetch-on-toggle is required).
// Hyperliquid gating is a read-time filter (via `select`), not part of the
// query key: the cached /networks response stays unfiltered, so flipping the
// hyperliquid-support flag re-derives instantly instead of refetching
const select = useCallback(
(networks: Networks): Networks =>
includeHyperliquid ? networks : filterOutHyperliquidNetworks(networks),
[includeHyperliquid]
)
return useQuery({
queryKey: [ReactQueryKeys.NETWORKS, includeSolana],
queryFn: () =>
29745b9 to
afbaa46
Compare
afbaa46 to
2b44701
Compare
…liquid-support feature flag and move them out of the Avalanche L1s section
…d use getQueryData in getNetworksFromCache
…ia select/getNetworksFromCache, restore 2-element networks query key
… filterNetworks call
2b44701 to
b5a14c4
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
packages/core-mobile/app/new/features/accountSettings/screens/manageNetworks/ManageNetworksScreen.tsx:131
- Same issue as above:
customis search-filtered, so using it to exclude custom networks can misclassify a custom chainId during search. Use the unfilteredcustomNetworkslist for the chainId check.
network =>
isHyperliquidNetwork(network) &&
!custom.some(item => item.chainId === network.chainId)
)
packages/core-mobile/app/new/features/accountSettings/screens/manageNetworks/ManageNetworksScreen.tsx:118
customis already filtered by search text viafilterNetworks(...), but it's also used to detect whether a chainId exists in custom networks. This means a custom network that doesn't match the current search can be treated as “not custom”, allowing the backend network with the same chainId to appear in the Avalanche L1s list. Use the unfilteredcustomNetworkslist for the de-dupe check instead ofcustom.
This issue also appears on line 128 of the same file.
!alwaysEnabledChainIds.includes(network.chainId) &&
!custom.some(item => item.chainId === network.chainId) &&
!defaultEnabledL2ChainIds.includes(network.chainId) &&
Description
Ticket: CP-14879
The backend started serving HyperEVM (chainId 999) — and potentially HyperCore (9999) — in
PROXY_URL/networksas part of the extension team's Hyperliquid launch (CP-14533 / CP-14848,display_in_coreflipped in core-token-aggregator). Mobile has no Hyperliquid support yet (CP-14595), but we render everything that endpoint returns, so HyperEVM appeared under "Avalanche L1s" in Manage Networks — and users with prior HyperEVM activity got it auto-enabled with balances via GlacierlistAddressChains. Extension/web protect themselves with a client-side PostHog gate; mobile had none.This PR:
hyperliquid-support(FeatureGates.HYPERLIQUID_SUPPORT, default OFF), following thesolana-supportpatternselectinuseGetNetworksplus the same sharedfilterOutHyperliquidNetworkspredicate ingetNetworksFromCache(which backs the redux selectors). The cached/networksresponse stays unfiltered and the query key stays[NETWORKS, includeSolana], so flipping the flag re-derives instantly from cache — no refetch, no cache fragmentationutils/network/isHyperliquidNetwork.tshelpers (+ tests)No new dependencies. Not breaking: users who already toggled HyperEVM on keep an inert
enabledChainIdsentry that resolves toundefinedand is filtered out safely.Screenshots/Videos
FF OFF

FF ON

Testing
Dev Testing (if applicable)
IOS: 9416
Android: 9417
hyperliquid-support(+everything) in PostHog → Networks screen shows a "Hyperliquid" section containing HyperEVM, NOT listed under "Avalanche L1s"NetworkService.test.ts(Hyperliquid include/exclude),isHyperliquidNetwork.test.ts,store/network/slice.test.ts— all passing (172 tests across touched suites)QA Testing (if applicable)
hyperliquid-supportis enabled in PostHogChecklist
Please check all that apply (if applicable)
🤖 Generated with Claude Code