Skip to content

CP-14879: hide Hyperliquid networks (HyperEVM/HyperCore) behind hyperliquid-support feature flag - #4005

Merged
B0Y3R-AVA merged 4 commits into
mainfrom
boyer/cp-14879
Jul 29, 2026
Merged

CP-14879: hide Hyperliquid networks (HyperEVM/HyperCore) behind hyperliquid-support feature flag#4005
B0Y3R-AVA merged 4 commits into
mainfrom
boyer/cp-14879

Conversation

@B0Y3R-AVA

@B0Y3R-AVA B0Y3R-AVA commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description

Ticket: CP-14879

The backend started serving HyperEVM (chainId 999) — and potentially HyperCore (9999) — in PROXY_URL/networks as part of the extension team's Hyperliquid launch (CP-14533 / CP-14848, display_in_core flipped 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 Glacier listAddressChains. Extension/web protect themselves with a client-side PostHog gate; mobile had none.

This PR:

  • Adds a new PostHog feature gate hyperliquid-support (FeatureGates.HYPERLIQUID_SUPPORT, default OFF), following the solana-support pattern
  • Strips Hyperliquid networks (chain ids 999/9999, plus chain-name match as defense) at the read layer: react-query select in useGetNetworks plus the same shared filterOutHyperliquidNetworks predicate in getNetworksFromCache (which backs the redux selectors). The cached /networks response stays unfiltered and the query key stays [NETWORKS, includeSolana], so flipping the flag re-derives instantly from cache — no refetch, no cache fragmentation
  • New utils/network/isHyperliquidNetwork.ts helpers (+ tests)
  • Manage Networks: Hyperliquid chains are excluded from the "Avalanche L1s" bucket and render in their own "Hyperliquid" section — which only appears once the flag is enabled

No new dependencies. Not breaking: users who already toggled HyperEVM on keep an inert enabledChainIds entry that resolves to undefined and is filtered out safely.

Screenshots/Videos

FF OFF
Screenshot 2026-07-27 at 5 04 08 PM

FF ON
Screenshot 2026-07-27 at 4 59 26 PM

Testing

Dev Testing (if applicable)

IOS: 9416
Android: 9417

  • Happy path: launch the app with default flags → Settings → Networks → HyperEVM/HyperCore must NOT appear anywhere (search included); portfolio must not show HyperEVM balances even for accounts with HyperEVM activity
  • Flag on: enable hyperliquid-support (+ everything) in PostHog → Networks screen shows a "Hyperliquid" section containing HyperEVM, NOT listed under "Avalanche L1s"
  • Edge case: account that had HyperEVM toggled on before this fix → no crash, network silently hidden while flag is off
  • Unit tests: 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)

  • Verify HyperEVM is gone from Manage Networks, network selector, send flow, and portfolio with production flags
  • Verify a wallet with HyperEVM transaction history does not auto-enable or display the network
  • Acceptance: HyperEVM/HyperCore are invisible in Core Mobile until hyperliquid-support is enabled in PostHog

Checklist

Please check all that apply (if applicable)

  • I have performed a self-review of my code
  • I have verified the code works
  • I have included screenshots / videos of android and ios
  • I have added testing steps
  • I have added/updated necessary unit tests
  • I have updated the documentation

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 22, 2026 14:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-support feature gate (default OFF) and Redux selector to block/allow Hyperliquid visibility.
  • Filtered Hyperliquid networks out of NetworkService.getNetworks and 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.

Comment on lines 5 to 15
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
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment on lines +14 to +24
export function isHyperliquidNetwork(network?: Network): boolean {
if (!network) {
return false
}

return (
isHyperliquidChainId(network.chainId) ||
network.chainName === HYPEREVM_CHAIN_NAME ||
network.chainName === HYPERCORE_CHAIN_NAME
)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in d1c2acf — chain-name fallback now trims and compares case-insensitively (still exact token match, so no false-positive risk).

Copilot AI review requested due to automatic review settings July 22, 2026 15:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread packages/core-mobile/app/store/network/slice.ts
Comment thread packages/core-mobile/app/store/network/slice.test.ts
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Coverage report ✅

2/2 packages passed thresholds
Thresholds are shown inline against each package baseline.

🟢 @avalabs/core-mobile

St. Category Percentage Covered / Total
🟢 Statements 27.88% (+9.88% vs 18% ▲) 12353/44297
🟢 Branches 23.54% (+11.54% vs 12% ▲) 6066/25767
🟢 Functions 22.18% (+9.18% vs 13% ▲) 2270/10234
🟢 Lines 27.96% (+9.96% vs 18% ▲) 11829/42300

🟢 @avalabs/k2-alpine

St. Category Percentage Covered / Total
🟢 Statements 8.49% (+6.49% vs 2% ▲) 304/3578
🟢 Branches 7.90% (+6.90% vs 1% ▲) 172/2177
🟢 Functions 4.99% (+3.99% vs 1% ▲) 41/821
🟢 Lines 7.82% (+5.82% vs 2% ▲) 257/3283
Artifacts and threshold sources
  • @avalabs/core-mobile: summary core-mobile/coverage/coverage-summary.json, thresholds core-mobile/coverage-thresholds.json
  • @avalabs/k2-alpine: summary k2-alpine/coverage/coverage-summary.json, thresholds k2-alpine/coverage-thresholds.json

Source run: Mobile PR

@ruijia1in ruijia1in left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-place delete is safe.
  • getQueryData swap (from .find()): more correct now that the key has 3 segments; all callers pass all 3.
  • React memo deps in ManageNetworksScreen are correct; the Hyperliquid section is gated implicitly via an empty networks map when the flag is off.

Comment thread packages/core-mobile/app/hooks/networks/useGetNetworks.ts Outdated
Comment thread packages/core-mobile/app/services/network/NetworkService.ts Outdated
Comment thread packages/core-mobile/app/utils/network/isHyperliquidNetwork.ts
Comment thread packages/core-mobile/app/utils/network/isHyperliquidNetwork.ts
Copilot AI review requested due to automatic review settings July 22, 2026 18:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread packages/core-mobile/app/store/posthog/types.ts Outdated
Copilot AI review requested due to automatic review settings July 22, 2026 19:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread packages/core-mobile/app/hooks/networks/useGetNetworks.ts
Copilot AI review requested due to automatic review settings July 22, 2026 19:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 flipping hyperliquid-support triggers a refetch. The implementation here explicitly keeps includeHyperliquid out of the query key and relies on a read-time select filter 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: () =>

Copilot AI review requested due to automatic review settings July 28, 2026 22:51
@B0Y3R-AVA
B0Y3R-AVA requested a review from a team as a code owner July 28, 2026 22:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

…liquid-support feature flag and move them out of the Avalanche L1s section
Copilot AI review requested due to automatic review settings July 29, 2026 13:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: custom is search-filtered, so using it to exclude custom networks can misclassify a custom chainId during search. Use the unfiltered customNetworks list 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

  • custom is already filtered by search text via filterNetworks(...), 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 unfiltered customNetworks list for the de-dupe check instead of custom.

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) &&

@B0Y3R-AVA
B0Y3R-AVA merged commit 1f9699d into main Jul 29, 2026
7 checks passed
@B0Y3R-AVA
B0Y3R-AVA deleted the boyer/cp-14879 branch July 29, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants