Repository navigation
fix(perps): preserve Lighter executed size on cancellation - #10308
Merged
Merged
Conversation
8 of 10 tasks
abretonc7s
marked this pull request as ready for review
September 21, 2026 10:18
abretonc7s
enabled auto-merge
September 21, 2026 12:37
geositta
approved these changes
Sep 22, 2026
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Sep 22, 2026
## Summary - Bump `@metamask/core-monorepo` to `1269.0.0` - Bump `@metamask/perps-controller` to `17.3.0` - Fixed: use Lighter's reported executed amount for order filled sizes so canceled orders do not appear fully filled (MetaMask#10308) ## Test plan - [x] `yarn changelog:validate` - [ ] CI 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Version and changelog updates only; trading behavior change was already merged in the referenced fix PR. > > **Overview** > This is a **release-only** PR: it bumps the monorepo to **`1269.0.0`** and publishes **`@metamask/perps-controller` `17.3.0`**, with matching **`CHANGELOG.md`** and compare-link updates. > > The **`17.3.0`** notes document the shipped fix from [MetaMask#10308](MetaMask#10308): **Lighter order filled sizes now use the venue’s reported executed amount**, so partially filled then **canceled** orders no longer show as **100% filled** in UI/state. > > There are **no runtime code changes** in this diff—only version metadata and release notes. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ea73c3b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Sep 23, 2026
…#34865) ## **Description** <!-- mms-check: type=text required=true --> Initial feature-gated Lighter POC using published `@metamask/perps-controller@17.3.0`, a pinned embedded WebView signer, and funded Lite order entry. Aggregated Lighter orders switch to Lighter before reading balance and market metadata. Hyperliquid retains its existing funding path. The follow-up constrains registration plaintext, blocks signer network APIs/subresources, recovers WASM/Go startup failures, cancels retired keychain/setup work, and records terminal failures. It also adds funded-screen telemetry, validates optional signer indices, collects the Metro test in CI, and prevents an unsupported Lighter deposit route. Controller 17.3.0 includes the upstream canceled-order filled-size fix; the temporary Yarn patch has been removed. [Capabilities, fees and POC limits](https://github.com/MetaMask/metamask-mobile/blob/TAT-3766-feat-lighter-webview-signer-poc/docs/perps/lighter-capabilities-and-fees.html) · [Pinned signer provenance and trust boundary](https://github.com/MetaMask/metamask-mobile/blob/TAT-3766-feat-lighter-webview-signer-poc/app/components/UI/Perps/Lighter/wasm-provenance.md) Controller upgrade verification at `fd68ef7d27`: installed unpatched **17.3.0**; **225 unit tests**, **6 component-view tests**, TypeScript, lint and formatting passed. Earlier trading captures retain the source/build provenance documented below. Bundle isolation at `365d4a12029`: `MM_PERPS_LIGHTER_PROVIDER_ENABLED=true` is now required in every build, including local development. Normal production already excluded the signer; development no longer includes it automatically. Real iOS bundles with the flag off contain no signer host/HTML in their source maps and no embedded WASM payload: production **20,804 modules**, development **21,098 modules**. Enabled POC builds still inline the local signer; this is build-time exclusion, not runtime code splitting. **235 focused unit tests**, **7 component-view tests**, TypeScript, lint and formatting passed. CI follow-up: Jest stubs the native signer asset instead of inlining 10 MB into each test VM. Component-view coverage now reports exercised files, matching the integration layer; unit tests retain repository-wide coverage. The previously failing shard passed locally with the same 12 GiB limit (**36 suites / 425 tests**) and all three component-view shards passed in CI on `903d04f13cc`. Merged main `638585d253f`, including its wallet compatibility fix from [MetaMask#36671](MetaMask#36671), and removed this PR's wallet-version override. No patch files or patch resolutions differ from main. **144 wallet-adapter and order-normalization tests**, immutable install and TypeScript passed after the merge. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Added initial Lighter integration with funded-balance order entry in Lite mode. ## **Related issues** Upstream controller fix: MetaMask/core#10308, merged and released in `@metamask/perps-controller@17.3.0`, now consumed directly without a Yarn patch. [Core live cancellation recipe](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/248fef4/docs/core-lighter-canceled-order.md) calls `PerpsController.getOrders()` directly: the same Lighter receipt fails with the old filled-size calculation and passes with the upstream fix, without placing new orders. <!-- mms-check: type=issue-link required=true --> Refs: MetaMask/core#10187 Tracking: TAT-3766. ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> 1. Set `MM_PERPS_LIGHTER_PROVIDER_ENABLED=true` in `.js.env` and restart Metro with a cleared cache, select an owned funded Lighter testnet account with an existing signer slot, and reserve an empty BTC market. 2. In Lite, submit a small market Long. Confirm the accepted size, then close through the UI and verify BTC is flat. 3. In Pro, place a non-crossing limit Long and cancel it individually. Verify no BTC orders/positions remain and canceled filled size is zero. 4. Open history and match the opening, closing and canceled records to their exact Lighter venue IDs. **Native build prerequisite:** the current branch requires `ExpoGlassEffect`, added to main on September 17. Runway `35154767219`, the newest downloadable iOS artifact checked on September 21, predates that module and can crash after unlock when Trade renders. Use a native development build matching this branch or a newer compatible Runway. When rebuilding an existing Runway fixture locally, preserve its keychain encryption configuration so saved signer credentials remain readable. Native compatibility follow-up on `717df71d4a`: installed a local iOS development build with `ExpoGlassEffect` and the original fixture encryption configuration. Actual lock/unlock and existing Lighter signer recovery passed **15/15** nodes; native module availability and the Trade menu passed **9/9**. No new key registration or trades in this follow-up. The trading receipts below retain their original build provenance. ### Executed Lighter proof iOS simulator `mm-1`, Runway development build `35154767219`, existing four-account fixture, `dev1`, Lighter testnet account **59**, existing slot **8**. No funding or key registration. The unrelated ETH position was preserved. Trading captures used the runtime source committed as `74d8077aa7`; only test formatting followed the captures. Post-commit launch verification and exact-row history validation passed. The library recipes explicitly select Lighter, assert the mounted order inputs, and reconcile run-scoped UI client IDs with Lighter's `accountInactiveOrders` endpoint and controller records bearing `providerId: lighter`. | Operation | UI client receipt | Lighter venue order | Result | |---|---|---|---| | Lite market Long | `68315437830547` | `844424924481651` | Filled `0.00012 BTC` | | UI close | `174228495428506` | `562949959175979` | Filled `0.00012 BTC` | | Pro limit/cancel | `258575485365276` | `844424924480362` | Canceled, filled size `0` | Market cycle: **39/39** nodes. Limit cycle: **35/35** nodes. History: **17/17** nodes. Both trading cycles ended with no BTC residue. The earlier failed receipt check is retained; Lighter's inactive-order read lagged cancellation acknowledgement, so the reusable assertion now polls only the exact IDs for a bounded period. The real iOS signer probe denied ten transport APIs and observed CSP violations for external image/script loads. Signing continued to work. No iframe CSP event was observed. Focused checks passed: **135 unit tests**, **7 funded-screen component-view tests**, TypeScript, lint, formatting and normal CI test discovery. Reusable recipes are now on the [Perps library main branch, pinned revision `32e9d79`](https://github.com/MetaMask/experimental-metamask-recipe-perps/tree/32e9d79). The [reproduction contract](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/32e9d79/docs/mobile-lighter-proof.md) covers venue selection, owned fixture/signer prerequisites, limits and cleanup. - [Explicit venue start state](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/32e9d79/recipes/mobile/perps/venue-start-state.recipe.json) - [Lite market open/close](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/32e9d79/recipes/mobile/perps/lighter-lite-market-cycle.recipe.json) - [Pro limit/cancel](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/32e9d79/recipes/mobile/perps/lighter-pro-limit-cycle.recipe.json) - [Exact-ID history](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/32e9d79/recipes/mobile/perps/lighter-history.recipe.json) The published limit recipe additionally refuses an ambiguous cancellation target across markets. Its final replay passed **38/38**, with Lighter order `844424924468408` canceled and filled size `0`. The screenshots above come from the earlier receipt table's runs. <details><summary>Replay the published recipes</summary> Use `@deeeed/metamask-harness@0.61.1` or newer. It includes `@farmslot/recipe-harness@0.18.1`, which retains typed invocation inputs for portable artifact validation. Use a funded, owned Lighter testnet fixture and an already registered key slot. Reserve the selected market, start with no BTC orders/positions, and restart the app before each trading journey. Set these variables to your fixture's public metadata; they are not credentials. ```sh export RECIPE_LIBRARY_PATH="perps=/path/to/experimental-metamask-recipe-perps" export METAMASK_RECIPE_AUTO_HUD=0 LIGHTER_PROOF_ARGS=( account="$FIXTURE_ACCOUNT" account_index="$LIGHTER_ACCOUNT_INDEX" api_key_index="$LIGHTER_KEY_SLOT" market=BTC market_index="$LIGHTER_BTC_INDEX" expected_leverage=2 min_spendable_usd=20 exclusive_testnet_market=true ) # Inspect each plan before replacing --plan with a new --artifacts-dir path. mm-harness run perps.lighter-lite-market-cycle "${LIGHTER_PROOF_ARGS[@]}" \ 'amount_usd="10"' --plan mm-harness run perps.lighter-pro-limit-cycle "${LIGHTER_PROOF_ARGS[@]}" \ 'amount_usd="20"' "limit_price=\"$NON_CROSSING_BID\"" --plan # Use the exact activity IDs returned by the receipt verification nodes. mm-harness run perps.lighter-history account="$FIXTURE_ACCOUNT" market=BTC \ open_activity_id="$OPEN_ACTIVITY_ID" close_activity_id="$CLOSE_ACTIVITY_ID" \ canceled_activity_id="$CANCELED_ACTIVITY_ID" --artifacts-dir temp/lighter-history-proof ``` </details> **Remaining limits:** Android runtime, mainnet financial trading, Extension compilation parity and a full audit/reproducible build of the deployed signer remain unverified. The registration protocol's L1 plaintext has no chain field; the pinned signer remains trusted for cryptography. The signer CSP now permits only the four embedded scripts by hash; Chromium and WebKit both loaded the real signer and blocked an extra inline script. The non-required PR-size check remains above its 1,000-line threshold. [All required checks pass on `c3666c30fe5`](https://github.com/MetaMask/metamask-mobile/actions/runs/35805884030/job/107011429452), including unit, component-view, integration, all Android smoke shards and Sonar. The full CI run on that revision completed successfully, including the Android performance follow-up. Bugbot passed with no unresolved review threads. CI and Bugbot are rerunning on `365d4a12029`; reviewer approval is still required. ### Review follow-up, `c25aa718c5` Wallet lock now retires pending signer work and removes the native WebView; unlocking creates a fresh page. The iOS wallet lifecycle recipe passed **15/15** nodes: locked keyring, zero native signers, rejected signing, then a new native page and successful read-token signing after unlock. Unit coverage includes pending work and rapid lock/unlock with stale page messages. The shared market router now selects the explicit Pro venue before mounting its order form and keeps the form closed on switch failure. Runtime proof recorded aggregated BTC metadata at Hyperliquid's 40x limit, switched to Lighter's 50x metadata, then placed and canceled the Lighter limit order. Client receipt `188515645512169` matched venue order `844424924447876`, canceled with filled size `0`; BTC ended flat. The combined run's final receipt node exceeded its bounded wait, and a separate exact-ID read subsequently passed. Both traces are retained. Focused unit/component-view checks, TypeScript and lint passed. Follow-up `717df71d4a` keeps Lite available after a failed Pro venue switch; its regression failed before the fix and the six router view tests pass afterward. The obsolete profiling-analyzer discussion and the Lite-mode follow-up were replied to and resolved. ## **Screenshots/Recordings** <table> <tr><td colspan="2"><strong>Recipe visual evidence</strong><br/>Screenshots are grouped for quick reviewer comparison.</td></tr> <tr> <td align="center" width="50%"><strong>Lighter: market Long filled, 0.00012 BTC</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/01-lighter-market-fill.png" alt="Lighter: market Long filled, 0.00012 BTC" width="400" /></td> <td align="center" width="50%"><strong>Lighter: full-position UI close</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/02-lighter-close.png" alt="Lighter: full-position UI close" width="400" /></td> </tr> <tr> <td align="center" width="50%"><strong>Lighter: resting 0.00029 BTC limit at $70,000</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/03-lighter-resting-limit.png" alt="Lighter: resting 0.00029 BTC limit at $70,000" width="400" /></td> <td align="center" width="50%"><strong>Lighter: individual cancel, no open BTC orders</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/04-lighter-cancel.png" alt="Lighter: individual cancel, no open BTC orders" width="400" /></td> </tr> <tr> <td align="center" width="50%"><strong>Lighter: exact opening and closing fills</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/05-lighter-trade-history.png" alt="Lighter: exact opening and closing fills" width="400" /></td> <td align="center" width="50%"><strong>Lighter: canceled limit and filled market orders</strong><br/><img src="https://raw.githubusercontent.com/abretonc7s/mm-mobile-artifacts/main/evidence/TAT-3766-dev-lighter-main-migration-1459/06-lighter-order-history.png" alt="Lighter: canceled limit and filled market orders" width="400" /></td> </tr> </table> ### **After** <!-- mms-check: type=screenshot required=true --> Actual Lighter testnet trades, backed by the exact venue receipts above. The shared UI does not display a venue label. The developer control remains visible. ### **Before** N/A. Initial provider integration. ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed MetaMask Contributor Docs and Coding Standards. - [x] I've completed the PR template to the best of my ability. - [x] I've included tests if applicable. - [x] I've documented my code using JSDoc format if applicable. - [x] I've applied the right labels on the PR. #### Performance checks (if applicable) - [x] I've assessed Android testing: not run for this iOS POC; remains a validation gap. - [x] I've assessed power-user testing: not run; no performance sign-off claimed. - [x] I've assessed Sentry tracing: funded-screen measurement added and tested; production delivery unverified. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR. - [ ] I confirm the acceptance criteria and testing evidence are sufficient. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Changes add off-device-adjacent cryptographic signing, private-key persistence, and a pinned WASM trust boundary—core wallet and trading security surface. > > **Overview** > Adds **feature-gated Lighter perps configuration** in `.js.env.example` (`MM_PERPS_LIGHTER_PROVIDER_ENABLED` plus optional testnet account and API-key index overrides). > > Introduces a **mobile Lighter signing stack**: a hidden **WebView** loads a **local embedded Go/WASM page** (network APIs stubbed, navigation restricted to `https://localhost/`, hash-locked CSP) and talks to React Native via a strict **postMessage** protocol. A module-level **`lighterSignerBridge`** implements the `@metamask/perps-controller` signer interface—queues work until the page is **ready**, persists signer keys in **SecureKeychain** (chain/account/slot scoped), validates **`Register Lighter Account`** plaintext before registration reaches the controller, and enforces **wallet-lock** behavior by tearing down the WebView and failing in-flight calls. > > **Lifecycle hardening** includes exponential-backoff **reloads** after WASM init or renderer crashes (with reload counters reset after a successful ready), **terminal unavailability** after repeated failures, and extensive unit tests (including a lightweight HTML mock for CI). **`wasm-provenance.md`** documents the pinned testnet WASM artifact and trust boundaries. A small **`lighterOrderNormalization`** test guards the upstream canceled-order filled-size behavior in the installed controller adapter. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c3666c3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This branch was successfully deployed
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.
Explanation
Lighter clears
remaining_base_amounton cancellation, soinitial - remainingincorrectly reports an unfilled canceled order as fully filled. Prefer the validated optionalfilled_base_amount; retain the existing calculation when the field is omitted.References
Live controller proof
The recipe invokes
PerpsController.getOrders()through the real Lighter provider and HTTP client. No Jest or mocked venue responses. The existing Mobile signer supplies read-token signatures; venue writes and transaction signing are blocked.Same testnet account 59, key slot 8, BTC cancellation 844424924447876, client receipt 188515645512169:
The published library recipe passed 4/4 steps on the pushed Core commit. No orders, funding or key registration were needed. Reproduction inputs and signer requirements are in the linked contract.
Validation
orderCalculations.advanced-orders.test.ts; no unrelated type fixes are included.Checklist
Note
Low Risk
Localized Lighter order normalization and display logic with backward-compatible fallback when filled_base_amount is omitted; no trading, auth, or signing path changes.
Overview
Fixes incorrect fully-filled display for Lighter orders that were canceled with zero remaining size but no execution. The adapter no longer infers fill size only from
initial - remaining; when the venue sendsfilled_base_amount, that value drivesfilledSize(including0on cancel). If the field is absent, the previous derivation is unchanged.Validation and types: optional
filledBaseAmountis accepted on inactive/active order payloads with strict decimal checks; malformed values still fail as invalid venue data.Tests: Jest covers adapter and client parsing; a new tsx e2e driver asserts
PerpsController.getOrdersmatches live testnet inactive-order wire data for a known canceled receipt (read-only, no venue writes).Reviewed by Cursor Bugbot for commit c1cbfda. Bugbot is set up for automated code reviews on this repo. Configure here.