feat(perps): Lighter venue POC — WebView WASM signer bridge and recovery surface - #34865
feat(perps): Lighter venue POC — WebView WASM signer bridge and recovery surface#34865abretonc7s wants to merge 32 commits into
Conversation
React Native's MessageEvent (polyfilled globally for the HyperLiquid WebSocket SDK) omits the source getter, but @metamask/post-message-stream's WindowPostMessageStream asserts its existence at module load, which breaks snaps execution-service init and prevents Engine startup in dev builds. Define a null getter — the window stream is never used at runtime on React Native, only its load-time assert matters.
…der exposure Adds the Lighter signer transport (TAT-3766 POC): a hidden WebView hosting the locally-bundled Go/WASM signer page (built from elliottech/lighter-go web-wasm source, base64-embedded, no network access) speaking the reference postMessage protocol, exposed to @metamask/perps-controller through a queue-behind-readiness bridge singleton injected via platform dependencies. Surfaces the Lighter venue in the provider selector (env-gated via MM_PERPS_LIGHTER_PROVIDER_ENABLED, testnet only) and scopes a babel-plugin-inline-import override for the signer HTML. Requires the Lighter-enabled @metamask/perps-controller build (POC branch; delivered locally via yalc — package.json intentionally not switched).
The bridge moves off PerpsPlatformDependencies onto providerCredentials.lighter.signerBridge so the shared platform surface stays venue-agnostic (matches the core-side change).
- Reject all pending signer calls when the WebView reloads (content process loss) and add a per-call 60s timeout so callers retry instead of hanging against a dead page - Redact the venue private key inside the WASM wrapper before results cross the postMessage boundary; signing stays inside the WebView - Update the provider selector test to the Lighter rows (MYX rows were removed from the selector when Lighter replaced it as second venue)
…producible dependency - lighterSignerBridge: reset now rejects callers waiting on the old readiness promise (their page-side state is gone) and every call is bounded by a 90s timeout that covers the pre-ready wait; the WebView also recovers on Android render-process loss and on load errors - The signer bridge is supplied to the controller only when the same env flag that mounts the WebView is set, so core's enablement gate and the client's signer mount can never disagree - Commit the Lighter-enabled perps-controller build under .yalc (with package.json already pointing at it) so the branch builds from a clean checkout until the core PR merges and publishes
Queued-call execution, reject-on-reload for waiters, and the never-mounted timeout.
…iene - The bridge exposes onReset so the controller invalidates its cached signer session the moment the WebView resets, instead of learning from the next failed trading call - Mark the committed .yalc payload linguist-generated/-diff so git diff --check passes; refresh it from core 3f0608abc0
Side-only venues (Lighter) report a closed short as a plain Buy with the realized pnl attached; the Buy branch previously showed only the fee and dropped the PnL. Any fill carrying nonzero PnL now renders through the close/PnL display path. Tests cover the Lighter Buy-with-PnL fill and the unchanged zero-PnL Buy.
…idation preview - A side-only Buy/Sell fill carrying realized PnL is categorized as a position close with PnL display; generic Bought/Sold titles stay untouched (cross-provider contract). Lighter fills with position context arrive as Close Short/Close Long and render natively — tests cover Open Short, Close Long, buy-close fallback, and zero-PnL open - Provider capability-gate errors (liquidation preview unavailable) render the explicit price fallback instead of a misleading 0.00
Flips now size the post-flip position as |trade size| - |signed startPosition| — a short flipped by a buy previously displayed the sum instead of the remainder. Tests cover Long > Short and Short > Long with asymmetric values.
A zero-PnL side-only Lighter fill (break-even reduction and an add both fit) gets a new neutral 'trade' category instead of guessing open (Buy) or close (Sell); HyperLiquid's spot Buy/Sell semantics are unchanged. Fills carrying realized PnL keep the close category and PnL display.
- usePerpsRecoveryStatus hook: reads pending manual TP/SL recoveries and recovered dispatch outcomes from PerpsController; explicit per-outcome acknowledge that refreshes after acknowledging; read errors surface instead of degrading to empty lists. - PerpsRecoveryStatusBanner: warning banner for a recovered dispatch (succeeded vs unknown wording) with an Acknowledge action wired to the stable recovery id, and a danger banner for parked manual TP/SL recovery naming the affected symbol; renders nothing when idle. - Rendered account-wide in PerpsHomeView; en locale strings added.
…sh, visible errors - usePerpsRecoveryStatus re-reads on screen focus (useFocusEffect) and on selected-account / active-provider / network changes, not just mount, so returning from a failed trade exposes fresh quarantine and manual state. - Acknowledgment failures are recorded into the hook's error state and rethrown; read failures keep surfacing instead of degrading to empty lists. - PerpsRecoveryStatusBanner renders read/acknowledgment errors as a danger banner (never returns null on error) while the dispatch banner stays rendered and actionable. - Tests: focus re-read, account/provider-change re-read, error banner, ack-failure visibility and propagation.
- Every usePerpsRecoveryStatus refresh takes a request generation and captures the selected-address/provider/network context; lists, error, and loading commit only for the latest request whose context still matches, so a slow stale read (or stale failure) can no longer overwrite a newer request's state. - Proof: mount request hangs, focus request commits fresh data, the stale request resolves late with empty data — the fresh state survives and loading settles.
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
PR template — items to address before "Ready for review"Warnings — informational, address before merging:
See docs/readme/ready-for-review.md for the full Definition of Ready for Review. |
🧪 Flaky unit test detectionRun history flaky detectionHistorical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow. Failures / runs sampled per window:
AI-detected flaky patterns
|
The committed .yalc payload and yarn.lock had drifted (immutable install failed on CI with a resolution hash mismatch). Commit the current controller build — carrying the reviewed durable-settlement, recovery-surface, and mainnet-gate changes — together with the matching lockfile. Interim until the published package replaces the vendored payload.
…tation - Stub PerpsRecoveryStatusBanner in the PerpsHomeView view test: this file's useFocusEffect mock invokes callbacks synchronously during render, which loops against the banner hook's focus refresh; the banner has its own unit suite. - createMobileClientConfig default expectation gains the disabled lighter credentials block the builder has emitted since the POC wiring (first CI run where unit tests executed on this branch).
- babel-config-paths gains the LighterSignerWebView inline-import override row the POC added to babel.config.js. - The order-lifecycle provider-selection phase reflects the selector list where the MYX rows were replaced by Lighter: MYX stays absent even with its flag enabled, and the switch flow selects the aggregated provider instead.
…ntroller Lighter venue-key derivation signs an EIP-191 message through the controller messenger; only signTypedMessage (HyperLiquid's method) was delegated, so first-trade readiness failed on device with 'A handler for KeyringController:signPersonalMessage has not been delegated'. Found by on-device validation; with the delegation the venue key registers on-chain from the app.
|
…MERGE holding area)
…d suite branching
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 585abb8. Configure here.
…ation in infra test, market-specific history assert
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Key observations:
SmokePerps: Directly impacted - PerpsController is the core controller for all perps functionality (Add Funds, positions, orders, withdrawals, limit orders, stop-loss, liquidation, etc.). All perps smoke tests exercise this controller. SmokeWalletPlatform: Per tag description, Perps is a section inside the Trending tab. Changes to PerpsController affect the Perps views embedded in Trending (PerpsHomeView, PerpsMarketListView, etc.). SmokeConfirmations: Per SmokePerps tag description, Add Funds deposits are on-chain transactions requiring confirmations. Must be selected alongside SmokePerps. The Performance Test Selection: |
⚡ Performance Test Results
Branch: |





Description
Description
Mobile side of the Lighter perps venue spike (TAT-3766, core PR: MetaMask/core#9889). Draft to preserve state — depends on the
@metamask/perps-controllerupdate landing first (currently consumed via a vendored.yalcpayload, to be replaced by the published package before review).What's included
providerCredentials.lighter.signerBridge(postMessage protocol, key material never leaves the bridge boundary). Signer-reset detection with proactive re-setup, timeout recovery, and key redaction in logs.MM_PERPS_LIGHTER_PROVIDER_ENABLED.usePerpsRecoveryStatushook +PerpsRecoveryStatusBannerin the Perps home view — surfaces parked manual TP/SL recoveries and recovered dispatch outcomes from the controller, with explicit per-outcome acknowledgment. Refreshes on screen focus and account/provider/network changes; generation-guarded against out-of-order async commits; read/acknowledgment errors render visibly instead of degrading to "nothing pending".Status
TAT-3766-feat-spike-lighter-perps-integratio).@metamask/perps-controllerrelease replacing the.yalcpayload.Related issues
Spike: TAT-3766
Manual testing steps
MM_PERPS_LIGHTER_PROVIDER_ENABLED=true.Pre-merge author checklist
Changelog
CHANGELOG entry: null
Related issues
Fixes: TAT-3766
Manual testing steps
Automated equivalent (recorded, 76/76 nodes):
Screenshots/Recordings
Before
Lighter venue not available; Perps trades only on HyperLiquid.
After
On-device validation evidence (Lighter testnet, iOS simulator)
Recorded by the composable recipe suite driving the real UI (no controller shortcuts for trade flows) — latest run 76/76 nodes green, venue verified flat afterwards:
🎬 Full demo video (5 m 22 s, HUD narrates each step)
Trade lifecycle — every step through the real screens
Venue rules are dynamic + provider-served surfaces
Evidence + recipes live under
recipes/as a DO-NOT-MERGE holding area (spike content, removed before merge; recipes graduate to the harness perps library).Pre-merge author checklist
Performance checks (if applicable)
Pre-merge reviewer checklist
Note
Medium Risk
Vendoring a large generated perps/trading controller changes dependency resolution and review surface until replaced by a published package; runtime trading behavior follows whatever is in the committed build.
Overview
Temporary build unblock for TAT-3766: the branch vendors a prebuilt
@metamask/perps-controller(12.0.0) under.yalc/@metamask/perps-controller(fulldist/plus package metadata) so mobile can consume the Lighter-capable controller before MetaMask/core publishes a preview release.Git hygiene for the vendored tree:
.gitignoreis updated so only this package path is tracked (parent.yalcstays ignored, with explicit exceptions forperps-controllerand itsdist/)..gitattributesmarks.yalc/@metamask/perps-controller/**as generated and excludes it from diff review.This is intended to be removed once
@metamask/perps-controlleris published and the app depends on the registry version again (per PR notes).Reviewed by Cursor Bugbot for commit 8db3497. Bugbot is set up for automated code reviews on this repo. Configure here.