Repository navigation
fix(perps-controller): tighten Lighter data validation - #10015
Merged
Merged
Conversation
abretonc7s
marked this pull request as ready for review
August 31, 2026 02:25
abretonc7s
temporarily deployed
to
default-branch
August 31, 2026 02:25 — with
GitHub Actions
Inactive
abretonc7s
enabled auto-merge
August 31, 2026 03:07
michalconsensys
approved these changes
Aug 31, 2026
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Aug 31, 2026
## Explanation Releases `@metamask/perps-controller` **14.0.0 → 15.0.0**. The monorepo version goes **1217.0.0 → 1218.0.0**. No other package is being published. Perps-controller has no in-monorepo dependents that need a workspace range bump. The bump is **major**. Adding a new member to the public `PerpsProviderType` union is a breaking type change for exhaustive switches: - **Added (breaking):** Lighter as a perps venue (initial implementation, disabled by default) ([MetaMask#9889](MetaMask#9889), [MetaMask#10015](MetaMask#10015)). `PerpsProviderType` gains `'lighter'`. Enablement requires client opt-in (`providerCredentials.lighter.enabled`, or the `perpsLighterProviderEnabled` remote feature flag plus a client-supplied `providerCredentials.lighter.signerBridge`). Without a bridge the provider is read-only. Lighter is off by default, so clients that do not opt in can upgrade without runtime behavior change; exhaustive `PerpsProviderType` switches still need a new case to typecheck. - **Fixed:** Preserve accepted HyperLiquid Scale orders when part of a batch is rejected ([MetaMask#9989](MetaMask#9989)). ## Changelog Moved Unreleased entries in `packages/perps-controller/CHANGELOG.md` under `[15.0.0]`. Folded the pre-release Lighter validation follow-up (MetaMask#10015) into the Lighter Added entry (as that PR intended). Breaking entry is prefixed with `**BREAKING:**` and includes adaptation notes. ## References - Source PRs: [MetaMask#9889](MetaMask#9889), [MetaMask#10015](MetaMask#10015), [MetaMask#9989](MetaMask#9989) - No draft Mobile/Extension adoption PRs required to keep current clients compiling: Lighter stays disabled until clients opt in. Client work to enable Lighter (credentials, signer bridge, UI) is separate. ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [x] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
3 of 4 tasks
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Sep 4, 2026
## Explanation Lighter retains inactive markets for historical identity and can zero their order minimums and margin fractions. The strict response decoder rejected those retired rows, which prevented otherwise valid active markets from loading. This change accepts zero constraints only for inactive rows, omits retired markets whose risk metadata cannot produce canonical market data, and preserves inactive rows with valid metadata as delisted. Positions left on retired markets remain readable using their current position leverage as a non-tradable cap. Active markets remain strict and fail closed on zero or malformed constraints. Validated with: - 322 focused Lighter service/provider tests - Full `@metamask/perps-controller` test suite and Core build - Targeted ESLint, formatting, and changelog validation - Mobile yalc integration using package `16.1.0` (`yalc.sig` `ee73aeec8330f58ce0a6e9c6b4cf42a3`) - A 36/36-node iOS recipe covering Lighter and Hyperliquid on mainnet and testnet - Revalidated Lighter mainnet on iOS `mm-1`: live markets loaded and the invalid inactive-market metadata error was absent ## References - Related to https://consensyssoftware.atlassian.net/browse/TAT-3863 - Related to https://consensyssoftware.atlassian.net/browse/TAT-3766 - Unblocks MetaMask/metamask-mobile#34865 - Follow-up to MetaMask#9889 and MetaMask#10015 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them — not applicable; this change is non-breaking <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Scoped to Lighter public market-metadata parsing and listing; active markets remain fail-closed on bad constraints, with no changes to signing, deposits, or position operations. > > **Overview** > Fixes Lighter market loading when the venue returns **inactive** perp rows with zeroed minimum order sizes or margin fractions—metadata Lighter keeps for history but that previously made the strict HTTP decoder reject the whole response and block active markets. > > **`LighterClientService`** now allows zero minimums and margin fractions only when `status === 'inactive'`, with `refine` validators still requiring positive values for **active** markets. **`LighterProvider`** adds `isInactiveMarketWithoutUsableRiskMetadata` to drop those retired rows from `getMarketDataWithPrices` and margin caching, skips them in `getMarkets` when leverage metadata is omitted (instead of throwing), and still surfaces inactive markets with valid risk data as **delisted**. Tests cover both omission and preservation paths. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 445456c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Reject malformed Lighter WebSocket fill fields and invalid margin fractions before they reach perps state. Adds coverage for inactive-history cursor nonprogress found during the final #9889 review.
Changelog
The existing unreleased Lighter entry already covers this pre-release correction.
Related issues
Follow-up to #9889.
Testing
Note
Medium Risk
Changes leverage derivation and fill ingestion boundaries for Lighter; overly strict validation could reject otherwise-usable venue rows or block market/position reads until metadata is fixed.
Overview
Tightens Lighter venue data handling so bad margin metadata and trade payloads fail before they affect markets, leverage, or fill streams.
Max leverage is now derived through a shared
deriveLighterMaxLeveragehelper that requiresminInitialMarginFractionto be a safe integer in 1–10,000 and rejects leverage below 1x.getMarkets, margin cache refresh,getMaxLeverage, and internal leverage lookups use this path; refresh also validates maintenance margin fractions in the same range. REST parsing swaps generic positive numbers for aMarginFractionStructon initial/default/maintenance margin fields.Fill adaptation (
adaptFillFromLighterTrade) adds checks on trade identifiers, requiresisMakerAskto be a boolean, and parses maker/taker fees viaparseLighterTradeFeeso malformed counterparty fee fields invalidate the row while still allowing valid nonzero counterparty fees. Matching WebSocket fill drops and unit tests cover these cases.Tests add rejection paths for out-of-range/non-integer margin fractions, malformed fill fields, and inactive-order history paging that advances the cursor with zero rows (TP/SL recovery fails closed).
Reviewed by Cursor Bugbot for commit 6f8126d. Bugbot is set up for automated code reviews on this repo. Configure here.