Commit 93c5524
authored
feat(perps): sync controller code with extension (#28509)
## **Description**
Makes `@metamask/perps-controller` safe to consume from the MetaMask
extension without pulling `@myx-trade/sdk` into the static webpack
bundle or violating the LavaMoat policy. Mobile owns the source-of-truth
for the perps controller package via
`scripts/perps/validate-core-sync.sh`, so the fix is made in mobile and
then rsync'd to core on a matching branch.
Companion core PR: MetaMask/core#8398 —
supersedes MetaMask/core#8374.
### What changed
1. **`app/controllers/perps/PerpsController.ts`** — add `/*
webpackIgnore: true */` magic comment to the `MYXProvider` dynamic
`import()` so webpack (extension) skips static resolution of the
intentionally-unshipped module. Metro (mobile) ignores the magic
comment, so runtime behavior on mobile is unchanged.
2. **`app/controllers/perps/index.ts`** — remove 8 MYX adapter functions
from the public barrel (`adaptMarketFromMYX`, `adaptPriceFromMYX`,
`adaptMarketDataFromMYX`, `filterMYXExclusiveMarkets`,
`isOverlappingMarket`, `buildPoolSymbolMap`, `buildSymbolPoolsMap`,
`extractSymbolFromPoolId`). These are still used internally by
`MYXProvider`, which imports them via relative path
`../utils/myxAdapter`. No runtime change in mobile — the functions are
still available to MYXProvider.
3. **`app/controllers/perps/utils/index.ts`** — drop the `export * from
'./myxAdapter'` barrel re-export so the utils index no longer
transitively re-exports MYX symbols.
4. **Drift fix — `perpsConnectionAttemptContext`** — moved from
`app/util/perpsConnectionAttemptContext.ts` into
`app/controllers/perps/utils/perpsConnectionAttemptContext.ts` and
exported from `@metamask/perps-controller` so:
- `HyperLiquidClientService` (inside the perps package) imports it via a
relative path that stays inside the synced directory — required for the
core sync to build.
- `PerpsConnectionManager` (outside the perps package) imports it from
`@metamask/perps-controller` — satisfies the `no-restricted-imports`
rule for code outside `app/controllers/perps/`.
5. **`.eslintrc.js` — align mobile perps override with core's base
rules.** Added `BinaryExpression[operator='in']`, `WithStatement`, and
`SequenceExpression` selectors to the existing `no-restricted-syntax`
rule in the `app/controllers/perps/**` override. Mobile's override was
missing these, which meant `'x' in y` type-guards passed mobile lint
silently and then landed in core as new `no-restricted-syntax`
suppressions at sync time. The override now mirrors what
`@metamask/eslint-config` enforces in core, catching the violations at
source.
6. **Replace `'x' in y` with `hasProperty(y, 'x')` across 8
perps-controller files** — `HyperLiquidProvider.ts`,
`HyperLiquidSubscriptionService.ts`, `TradingService.ts`,
`marketDataTransform.ts`, `hyperLiquidAdapter.ts`, `types/index.ts`,
`types/transactionTypes.ts`, `utils/errorUtils.ts`. Uses `hasProperty`
from `@metamask/utils` (the idiomatic replacement documented in core's
eslint config). 24 of the 28 occurrences were converted cleanly; the
remaining 4 are kept as `'in'` behind `/* eslint-disable
no-restricted-syntax */` blocks because `hasProperty` narrows the KEY
but not the discriminated-union branch — losing access to `.filled`,
`.resting`, `.oid`, `.totalSz` on the HyperLiquid SDK types and losing
`keyof typeof HYPERLIQUID_ASSET_CONFIGS` narrowing. Each disable comment
is documented with the narrowing rationale.
7. **`scripts/perps/validate-core-sync.sh` — hardened preflight +
per-file suppression delta check.**
- **Preflight freshness check** (`chore(perps-sync): hard-fail sync
script when core main has drift`): the previous preflight only compared
the core working tree against `.sync-state.json.lastSyncedCoreCommit`,
so it could not detect that someone else had merged a competing
perps-controller change into `origin/main` while this branch was in
review. That gap let [#8398](MetaMask/core#8398)
land in a conflicting state with
[#8333](MetaMask/core#8333). The new check at
the top of `step_conflict_check` fetches `origin/main`, runs `git
rev-list --count HEAD..origin/main -- packages/perps-controller/`, and
**hard-fails** (not warns) with the offending commit list + a merge
suggestion if any such commits exist. Gracefully degrades to `WARN` if
offline or if `origin/main` is missing.
- **Per-file/per-rule suppression delta check** (`chore(perps-sync):
hard-fail on per-file suppression delta increase`): step 6 now snapshots
the per-file/per-rule suppression counts from `eslint-suppressions.json`
BEFORE running `--fix` / `--suppress-all` / `--prune-suppressions`, then
diffs against the post-run counts and hard-fails if any (file, rule)
pair's count INCREASED. Reducing counts (mobile fixes removing
previously-suppressed violations) is always allowed. Increases mean the
current sync is introducing NEW violations that would land in core as
fresh suppressions and must be fixed at source. The script prints every
offending file+rule pair and points at `hasProperty()` from
`@metamask/utils` as the canonical replacement. Replaces the old "WARN
if count > 20" heuristic. This is the canonical local detection point
for the problem that *"it should have been detected locally!"* — a
violation now fails the sync script at step 6 BEFORE the core PR is
opened.
### Not included (intentional)
An earlier draft of this PR also declared
`MetaMetricsController:trackEvent` in `PerpsControllerAllowedActions` to
let the extension drop a type cast. That change was **reverted** because
mobile has no `MetaMetricsController` — it uses a `MetaMetrics`
singleton (`app/core/Analytics/MetaMetrics.ts`) — so adding the action
to the allowed-actions union forced `@metamask/messenger`'s parent type
to narrow to `never` and broke
`app/core/Engine/messengers/perps-controller-messenger/index.ts`. The
extension keeps its existing `as unknown as
PackagePerpsControllerMessenger` cast workaround until both host apps
share a real `MetaMetricsController`.
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes:
[TAT-2863](https://consensyssoftware.atlassian.net/browse/TAT-2863)
Related: MetaMask/core#8374 (superseded by
companion core PR #8398)
## **Manual testing steps**
```gherkin
Feature: Perps controller behavior unchanged after MYX export cleanup
Scenario: User opens the Perps tab on testnet
Given the wallet is unlocked and funded on HyperLiquid testnet
When user navigates to the Perps tab
Then the markets list loads
And the HyperLiquid provider is selected by default
Scenario: User opens and closes a BTC long position
Given the wallet has testnet funds
When user opens a $11 BTC long market order
Then a position appears in the positions list
When user closes the position at 100%
Then the position is removed from the positions list
```
Automated verification (all passing locally):
- `yarn lint` on all touched files — clean
- `NODE_OPTIONS='--max-old-space-size=8192' npx tsc --noEmit` — clean
(full, no incremental cache)
- `yarn jest app/controllers/perps/PerpsController.test.ts
app/controllers/perps/services/HyperLiquidSubscriptionService.test.ts
app/controllers/perps/services/TradingService.test.ts` — 412 passing
- `bash scripts/perps/validate-core-sync.sh --core-path …/core` — all 9
steps PASS. Suppression count drops from 30 → **6** after the lint
cleanup.
## **Screenshots/Recordings**
N/A — this PR is a bundler / import-graph + lint cleanup with zero UI
impact. Mobile runtime behavior is unchanged because:
- The `webpackIgnore` magic comment is extension/webpack-only; Metro
ignores it.
- The 8 removed exports are still used internally by `MYXProvider` via
relative import.
- `perpsConnectionAttemptContext` keeps its module-level state, just at
a new file path.
- `hasProperty(obj, key)` is runtime-equivalent to `key in obj` for
plain objects (both delegate to `Object.prototype.hasOwnProperty`
semantics via the prototype chain).
## **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 (existing tests untouched; new
location for moved test file is covered by the same suite)
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] 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.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Medium risk because it changes perps controller module
boundaries/exports and touches trading/provider code paths (type-guard
refactors and a few scoped lint suppressions), plus alters sync script
behavior that can block releases if misconfigured.
>
> **Overview**
> Improves extension-safe consumption of `@metamask/perps-controller` by
ensuring the optional `MYXProvider` stays excluded from webpack’s static
bundle and by removing MYX adapter re-exports from the public perps
barrels.
>
> Aligns mobile’s perps linting with core by restricting the `in`
operator (and a couple of other syntaxes) and refactors multiple perps
files to use `hasProperty()` instead of `'key' in obj`, keeping a few
documented `in` usages behind targeted `no-restricted-syntax` disables
where TypeScript narrowing is required.
>
> Moves/exports `perpsConnectionAttemptContext` under the perps utils
surface so in-package code imports remain sync-safe and out-of-package
callers use the package export, and hardens
`scripts/perps/validate-core-sync.sh` with an `origin/main` freshness
check plus a per-file/per-rule eslint suppression delta hard-fail
(including prettier formatting of `eslint-suppressions.json`).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1f90a9b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 905043c commit 93c5524
18 files changed
Lines changed: 175 additions & 41 deletions
File tree
- app
- components/UI/Perps/services
- controllers/perps
- providers
- services
- types
- utils
- scripts/perps
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
287 | 287 | | |
288 | 288 | | |
289 | 289 | | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
290 | 306 | | |
291 | 307 | | |
292 | 308 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
37 | | - | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1694 | 1694 | | |
1695 | 1695 | | |
1696 | 1696 | | |
1697 | | - | |
| 1697 | + | |
| 1698 | + | |
| 1699 | + | |
1698 | 1700 | | |
1699 | 1701 | | |
1700 | 1702 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
464 | 464 | | |
465 | 465 | | |
466 | 466 | | |
467 | | - | |
468 | | - | |
469 | | - | |
470 | | - | |
471 | | - | |
472 | | - | |
473 | | - | |
474 | | - | |
475 | | - | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
476 | 471 | | |
477 | 472 | | |
478 | 473 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
1968 | 1968 | | |
1969 | 1969 | | |
1970 | 1970 | | |
1971 | | - | |
| 1971 | + | |
1972 | 1972 | | |
1973 | 1973 | | |
1974 | 1974 | | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
| 1978 | + | |
1975 | 1979 | | |
1976 | 1980 | | |
1977 | 1981 | | |
1978 | 1982 | | |
| 1983 | + | |
1979 | 1984 | | |
1980 | 1985 | | |
1981 | 1986 | | |
| |||
3426 | 3431 | | |
3427 | 3432 | | |
3428 | 3433 | | |
| 3434 | + | |
| 3435 | + | |
| 3436 | + | |
| 3437 | + | |
3429 | 3438 | | |
3430 | 3439 | | |
3431 | 3440 | | |
3432 | 3441 | | |
| 3442 | + | |
3433 | 3443 | | |
3434 | 3444 | | |
3435 | 3445 | | |
| |||
4143 | 4153 | | |
4144 | 4154 | | |
4145 | 4155 | | |
4146 | | - | |
| 4156 | + | |
| 4157 | + | |
4147 | 4158 | | |
4148 | 4159 | | |
4149 | 4160 | | |
| |||
4153 | 4164 | | |
4154 | 4165 | | |
4155 | 4166 | | |
4156 | | - | |
| 4167 | + | |
4157 | 4168 | | |
4158 | 4169 | | |
4159 | 4170 | | |
| |||
4179 | 4190 | | |
4180 | 4191 | | |
4181 | 4192 | | |
4182 | | - | |
| 4193 | + | |
4183 | 4194 | | |
4184 | | - | |
| 4195 | + | |
4185 | 4196 | | |
4186 | 4197 | | |
4187 | 4198 | | |
| |||
5998 | 6009 | | |
5999 | 6010 | | |
6000 | 6011 | | |
6001 | | - | |
| 6012 | + | |
6002 | 6013 | | |
6003 | 6014 | | |
6004 | 6015 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | 10 | | |
12 | 11 | | |
13 | 12 | | |
| |||
20 | 19 | | |
21 | 20 | | |
22 | 21 | | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
Lines changed: 5 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
2583 | 2584 | | |
2584 | 2585 | | |
2585 | 2586 | | |
2586 | | - | |
2587 | | - | |
2588 | | - | |
| 2587 | + | |
| 2588 | + | |
| 2589 | + | |
2589 | 2590 | | |
2590 | 2591 | | |
2591 | 2592 | | |
| |||
2904 | 2905 | | |
2905 | 2906 | | |
2906 | 2907 | | |
2907 | | - | |
| 2908 | + | |
2908 | 2909 | | |
2909 | 2910 | | |
2910 | 2911 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1516 | 1516 | | |
1517 | 1517 | | |
1518 | 1518 | | |
1519 | | - | |
1520 | 1519 | | |
1521 | 1520 | | |
1522 | 1521 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
1643 | 1644 | | |
1644 | 1645 | | |
1645 | 1646 | | |
1646 | | - | |
1647 | | - | |
| 1647 | + | |
| 1648 | + | |
1648 | 1649 | | |
1649 | 1650 | | |
1650 | 1651 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
62 | 63 | | |
63 | 64 | | |
64 | 65 | | |
65 | | - | |
| 66 | + | |
66 | 67 | | |
67 | 68 | | |
68 | 69 | | |
| |||
74 | 75 | | |
75 | 76 | | |
76 | 77 | | |
77 | | - | |
| 78 | + | |
78 | 79 | | |
0 commit comments