Skip to content

fix(perps): investigate Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'#30612

Merged
abretonc7s merged 6 commits into
mainfrom
fix/tat-3223-fix-hyperliquid-closeevent
May 26, 2026
Merged

fix(perps): investigate Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'#30612
abretonc7s merged 6 commits into
mainfrom
fix/tat-3223-fix-hyperliquid-closeevent

Conversation

@abretonc7s

@abretonc7s abretonc7s commented May 26, 2026

Copy link
Copy Markdown
Contributor

Description

Fix TypeError: Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event' crash caused by the CloseEvent polyfill in shim.js using event-target-shim's Event class instead of React Native's own Event class. When @nktkas/rews (Hyperliquid SDK WebSocket transport) dispatches a CloseEvent on the native WebSocket, RN's dispatchEvent validates event instanceof RNEvent — which failed because event-target-shim provides a different Event class.

Replaced event-target-shim globals with React Native's own Event, EventTarget, CloseEvent, and MessageEvent classes so all instanceof checks pass consistently.

Changelog

CHANGELOG entry: Fixed a crash caused by CloseEvent dispatch on WebSocket failing instanceof validation

Related issues

Fixes: TAT-3223

Manual testing steps

Feature: CloseEvent dispatch on native WebSocket

  Scenario: CloseEvent is dispatched on native WebSocket without error
    Given the app is running with Hyperliquid SDK active

    When a WebSocket connection is closed while in CONNECTING state
    Then no TypeError is thrown
    And the CloseEvent is dispatched successfully

Screenshots/Recordings

State-only fix: no visual evidence needed. Both ACs proven via CDP eval (CloseEvent dispatch success) and lint:tsc (no TS errors).

Pre-merge author checklist

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.

Validation Recipe

recipe.json
{
  "pr": "TAT-3223",
  "title": "CloseEvent dispatchEvent on native WebSocket must not throw TypeError",
  "jira": "TAT-3223",
  "acceptance_criteria": [
    "CloseEvent dispatched on native WebSocket must not throw TypeError",
    "No new TypeScript errors introduced by the fix"
  ],
  "validate": {
    "static": ["yarn lint:tsc"],
    "workflow": {
      "pre_conditions": ["wallet.unlocked"],
      "entry": "ac1-eval-closeevent-dispatch",
      "nodes": {
        "ac1-eval-closeevent-dispatch": {
          "action": "eval_sync",
          "expression": "(function() { try { var ws = new WebSocket('wss://echo.websocket.org'); var ce = new CloseEvent('close', {code: 1006, reason: '', wasClean: false}); ws.dispatchEvent(ce); ws.close(); return JSON.stringify({success: true, error: null}); } catch(e) { return JSON.stringify({success: false, error: e.message}); } })()",
          "assert": { "operator": "eq", "field": "success", "value": true },
          "next": "ac1-eval-closeevent-props"
        },
        "ac1-eval-closeevent-props": {
          "action": "eval_sync",
          "expression": "(function() { var ce = new CloseEvent('close', {code: 1006, reason: 'test', wasClean: true}); return JSON.stringify({type: ce.type, code: ce.code, reason: ce.reason, wasClean: ce.wasClean}); })()",
          "assert": {
            "all": [
              { "operator": "eq", "field": "code", "value": 1006 },
              { "operator": "eq", "field": "reason", "value": "test" },
              { "operator": "eq", "field": "wasClean", "value": true }
            ]
          },
          "next": "ac1-eval-messageevent-dispatch"
        },
        "ac1-eval-messageevent-dispatch": {
          "action": "eval_sync",
          "expression": "(function() { try { var ws = new WebSocket('wss://echo.websocket.org'); var me = new MessageEvent('message', {data: 'hello'}); ws.dispatchEvent(me); ws.close(); return JSON.stringify({success: true, error: null}); } catch(e) { return JSON.stringify({success: false, error: e.message}); } })()",
          "assert": { "operator": "eq", "field": "success", "value": true },
          "next": "setup-done"
        },
        "setup-done": {
          "action": "end",
          "status": "pass"
        }
      }
    }
  }
}

Recipe Workflow

workflow.mmd
graph TD
    ac1-eval-closeevent-dispatch["ac1-eval-closeevent-dispatch<br/>eval_sync: CloseEvent dispatch on native WS"]
    ac1-eval-closeevent-props["ac1-eval-closeevent-props<br/>eval_sync: Verify CloseEvent properties"]
    ac1-eval-messageevent-dispatch["ac1-eval-messageevent-dispatch<br/>eval_sync: MessageEvent dispatch on native WS"]
    setup-done["setup-done<br/>end: pass"]

    ac1-eval-closeevent-dispatch --> ac1-eval-closeevent-props
    ac1-eval-closeevent-props --> ac1-eval-messageevent-dispatch
    ac1-eval-messageevent-dispatch --> setup-done
Loading

Note

Medium Risk
Touches app bootstrap polyfills used by Hyperliquid WebSockets; low blast radius but wrong globals could break perps connectivity at runtime.

Overview
Fixes a Hyperliquid / perps WebSocket crash where dispatchEvent rejected CloseEvent because polyfilled events did not pass React Native’s instanceof Event check.

shim.js stops using event-target-shim and hand-rolled CloseEvent / MessageEvent constructors. When globals are missing, it assigns React Native’s own Event, EventTarget, CloseEvent, and MessageEvent from RN private web API modules so events dispatched by @nktkas/rews match what RN’s WebSocket EventTarget expects.

event-target-shim is removed from package.json / lockfile. shim.test.js adds unit coverage for RN CloseEvent and MessageEvent properties and inheritance.

Reviewed by Cursor Bugbot for commit 5d5cb89. Bugbot is set up for automated code reviews on this repo. Configure here.

Replace event-target-shim's Event/EventTarget globals with React Native's
own implementations from react-native/src/private/webapis/. This fixes the
"parameter 1 is not of type 'Event'" TypeError when @nktkas/rews dispatches
CloseEvent on the native WebSocket, because RN's EventTarget.dispatchEvent
validates event instanceof using its own Event class.
@github-actions

Copy link
Copy Markdown
Contributor

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.

@metamaskbotv2 metamaskbotv2 Bot added the team-perps Perps team label May 26, 2026
@abretonc7s

abretonc7s commented May 26, 2026

Copy link
Copy Markdown
Contributor Author
Run Duration Model Nudges Grade Cost
e9f3a22a ? opus / claude 0 high / 7 $unknown
Worker report

Report — TAT-3223: Fix CloseEvent dispatchEvent TypeError

Summary

The CloseEvent polyfill in shim.js used event-target-shim's Event class, which is a different class than React Native's internal Event. When @nktkas/rews (Hyperliquid SDK WebSocket transport) dispatched a CloseEvent on the native WebSocket, RN's dispatchEvent rejected it because it failed the instanceof check against RN's own Event class. Fixed by replacing event-target-shim globals with React Native's own Event/EventTarget/CloseEvent/MessageEvent classes.

Root cause

shim.js:166 installed event-target-shim's Event/EventTarget as global.Event/global.EventTarget. The CloseEvent shim at shim.js:182 created events via new global.Event(type, params) — producing event-target-shim Event instances.

React Native's WebSocket (node_modules/react-native/Libraries/WebSocket/WebSocket.js:76) extends RN's internal EventTarget (node_modules/react-native/src/private/webapis/dom/events/EventTarget.js), whose dispatchEvent (line 193) validates event instanceof Event using its own imported Event class — a different class than event-target-shim's.

When @nktkas/rews/script/mod.js called this._socket.dispatchEvent(new CloseEvent(...)) (lines 340, 385) on the native WebSocket, the event-target-shim Event instance failed the RN instanceof check → TypeError thrown.

Reproduction commit

SHA: fb565b1c071debug(TAT-3223): add reproduction marker

Metro log excerpt:

WARN  [TAT-3223] BUG_MARKER: CloseEvent dispatchEvent FAILED - Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'.

Changes

  • shim.js — Replace event-target-shim Event/EventTarget with React Native's own classes from react-native/src/private/webapis/dom/events/. Replace function-based CloseEvent/MessageEvent shims with RN's proper class-based implementations.
  • shim.test.js (new) — Unit tests verifying RN CloseEvent/MessageEvent constructors produce correct properties and extend RN Event.

Test plan

Automated

  • yarn jest shim.test.js — 6/6 pass (CloseEvent + MessageEvent property access, RN Event inheritance)
  • yarn lint — pass (exit 0)
  • yarn lint:tsc — pass (exit 0)
  • yarn format:check — pass
  • Recipe validation — 3/3 nodes pass (CloseEvent dispatch on native WebSocket, property access, MessageEvent dispatch)

Manual (Gherkin)

Given the app is running with the Hyperliquid SDK connected
When a WebSocket connection is closed while in CONNECTING state
Then no TypeError "parameter 1 is not of type 'Event'" is thrown
And the CloseEvent is dispatched successfully on the native WebSocket

Evidence

  • recipe.json — Executable recipe proving CloseEvent/MessageEvent dispatch on native WebSocket succeeds
  • recipe-baseline.json — Baseline recipe proving the bug existed pre-fix (dispatch returned success: false)
  • recipe-coverage.md — 2/2 ACs PROVEN

Ticket

TAT-3223

@abretonc7s abretonc7s changed the title chore: prepare farmslot publication pkg-e9f3a22a-mplwi4b0 fix(perps): investigate Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event' May 26, 2026
@abretonc7s abretonc7s marked this pull request as ready for review May 26, 2026 00:45

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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 4d38898. Configure here.

Comment thread shim.js
Switched to RN native Event classes in prior commit, making
event-target-shim a dead direct dependency flagged by depcheck.
@abretonc7s abretonc7s enabled auto-merge May 26, 2026 14:13
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePreps
  • Risk Level: medium
  • AI Confidence: 82%
click to see 🤖 AI reasoning details

E2E Test Selection:
The PR makes targeted changes to the global shim.js polyfill layer:

  1. Removes event-target-shim package from package.json - this third-party package provided Event/EventTarget polyfills that were incompatible with React Native's WebSocket internals.

  2. Replaces with RN's own internal classes - shim.js now uses React Native's own Event, EventTarget, CloseEvent, and MessageEvent classes via deep imports. This fixes a bug (TAT-3223) where event-target-shim's Event class failed instanceof checks in RN's WebSocket.dispatchEvent, causing TypeErrors when the Hyperliquid SDK dispatched CloseEvent on native WebSocket.

  3. Adds unit tests (shim.test.js) verifying the polyfill behavior.

Impact Analysis:

  • The shim.js is loaded globally at app startup, so the polyfill change affects all WebSocket usage
  • The primary consumer is the Perps feature which uses the Hyperliquid SDK's WebSocket transport (@nktkas/rews) for real-time streaming of positions, orders, and account data
  • The PerpsWebSocketHealthToast component and WebSocket streaming hooks are directly dependent on these event types working correctly
  • The fix ensures CloseEvent and MessageEvent dispatched by the Hyperliquid SDK are compatible with RN's native WebSocket

Tag Selection:

  • SmokePerps: Primary target - Perps WebSocket streaming is the direct beneficiary of this fix; must validate the WebSocket connection works correctly
  • SmokeWalletPlatform: Required by SmokePerps description (Perps is a section inside Trending tab)
  • SmokeConfirmations: Required by SmokePerps description (Add Funds deposits are on-chain transactions)

Other tags (SmokeSwap, SmokeAccounts, SmokeBrowser, etc.) are not directly impacted as the WebSocket event polyfill change is specifically for the Hyperliquid SDK used by Perps.

Performance Test Selection:
The WebSocket event polyfill fix directly affects the Perps feature's real-time data streaming via the Hyperliquid SDK. Since CloseEvent and MessageEvent are now using RN's native classes instead of the shim package, the WebSocket connection lifecycle (connect, disconnect, reconnect) and data streaming performance could be affected. @PerformancePreps covers perps market loading, position management, and the add funds flow - all of which depend on the WebSocket connection working correctly and efficiently.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@abretonc7s abretonc7s added this pull request to the merge queue May 26, 2026
Merged via the queue into main with commit 6159478 May 26, 2026
195 of 200 checks passed
@abretonc7s abretonc7s deleted the fix/tat-3223-fix-hyperliquid-closeevent branch May 26, 2026 17:03
@github-actions github-actions Bot locked and limited conversation to collaborators May 26, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.80.0 Issue or pull request that will be included in release 7.80.0 label May 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.80.0 Issue or pull request that will be included in release 7.80.0 size-M team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants