Skip to content

fix(perps): address bugbot findings on Core PR #7941#26110

Merged
abretonc7s merged 7 commits intomainfrom
fix/perps/bugbotcore
Feb 20, 2026
Merged

fix(perps): address bugbot findings on Core PR #7941#26110
abretonc7s merged 7 commits intomainfrom
fix/perps/bugbotcore

Conversation

@abretonc7s
Copy link
Contributor

@abretonc7s abretonc7s commented Feb 16, 2026

Description

Defensive hardening for edge cases in PerpsController surfaced by static analysis on Core PR #7941. None of these bugs are currently triggerable in production — the app works correctly today — but all represent latent issues that could bite under future changes or unusual network conditions.

What changed

1. toggleTestnet() — false success on silent init failure

performInitialization() catches errors internally and sets InitializationState.Failed instead of throwing. toggleTestnet() was not checking for this, so it could return { success: true } even when the network switch failed. This patch:

  • Adds the same InitializationState.Failed check after await this.init()
  • Rolls back isTestnet to its previous value on failure

2. depositWithConfirmation() — never-resolving promise when placeOrder=true

The placeOrder path created new Promise(() => {}) — a promise that can never be GC'd and would hang any consumer who awaits it. Replaced with Promise.resolve(transactionMeta.id) for proper fire-and-forget semantics.

3. depositWithConfirmation() — failed deposits remain permanently pending

depositWithConfirmation adds a pending entry to state.depositRequests before validating networkClientId. If the validation throws, the deposit request stays status: 'pending' forever. This patch marks the deposit request as failed in the outer catch when a pre-submission error occurs.

4. Feature flag listener lost after disconnect

disconnect() unsubscribed RemoteFeatureFlagController:stateChange but no reconnect path re-subscribed. After disconnect → reconnect, geo-blocking and HIP-3 flag changes stopped propagating. The feature-flag subscription is a controller-lifetime concern (not session-lifetime), so we removed the unsubscribe from disconnect() entirely — the subscription now lives for the full controller lifecycle.

5. depositWithConfirmation() — deposit+order request stays pending forever

When placeOrder=true, the if (!placeOrder) guard skips the .then()/.catch() lifecycle block that transitions the deposit request to completed/failed. No other handler exists for this path. Added an else if branch that attaches lifecycle tracking to addResult.result (the real transaction promise) for the deposit+order flow.

6. Non-EVM account switch leaves stale cache

The account-change handler only cleared cached user data when currentAddress is truthy. Switching to an account group with no EVM account (e.g., Bitcoin-only) skipped cache clearing, leaving stale positions/orders visible. Restructured the condition so cache is always cleared when the account group changes, with preload guarded separately behind if (currentAddress).

Changelog

CHANGELOG entry: null

Related issues

Fixes: bugbot findings from Core PR #7941

Manual testing steps

These are defensive fixes for edge cases not reachable through normal UI flows. Verification is via unit tests.

Feature: Perps network toggle resilience

  Scenario: toggleTestnet reports accurate status when init fails silently
    Given user is on mainnet with perps initialized
    When user toggles to testnet and initialization fails internally
    Then toggleTestnet returns { success: false }
    And isTestnet is rolled back to its previous value

Feature: Perps deposit promise contract

  Scenario: depositWithConfirmation result promise resolves when placeOrder is true
    Given user initiates a deposit with placeOrder=true
    When the transaction is submitted
    Then the result promise resolves with the transaction ID

Feature: Perps deposit request lifecycle

  Scenario: deposit request is marked failed on pre-submission error
    Given user initiates a deposit and the deposit request is added as pending
    When the networkClientId lookup fails
    Then the deposit request status is updated to 'failed'

  Scenario: deposit+order request transitions from pending
    Given user initiates a deposit with placeOrder=true
    When the transaction completes or fails
    Then the deposit request status is updated to 'completed' or 'failed'

Feature: Feature flag subscription resilience

  Scenario: geo-blocking flags propagate after disconnect/reconnect
    Given perps controller is initialized with feature flag subscription
    When disconnect() is called and controller reconnects
    Then feature flag changes still propagate correctly

Feature: Account switch cache clearing

  Scenario: switching to non-EVM account clears stale perps cache
    Given user has cached perps data from an EVM account
    When user switches to a Bitcoin-only account group
    Then cached positions, orders, and account state are cleared

Screenshots/Recordings

N/A — no UI changes, logic-only hardening.

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.

- toggleTestnet: check InitializationState.Failed after init() and
  rollback isTestnet on failure (matching switchProvider pattern)
- depositWithConfirmation: replace never-resolving promise with
  Promise.resolve(transactionMeta.id) when placeOrder=true
@github-actions
Copy link
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.

@metamaskbot metamaskbot added the team-perps Perps team label Feb 16, 2026
@abretonc7s abretonc7s marked this pull request as ready for review February 16, 2026 11:48
@abretonc7s abretonc7s requested a review from a team as a code owner February 16, 2026 11:48
Hoist currentDepositId before try block so it is accessible in the
outer catch. When a pre-submission error occurs (e.g. missing
networkClientId), the deposit request is now marked as 'failed'
instead of staying permanently 'pending' in state.

Added test verifying the deposit request status on networkClientId
lookup failure.
@github-actions github-actions bot added size-M and removed size-S labels Feb 16, 2026
Prevent standalone preload from leaking WebSocket providers by caching
the HyperLiquidProvider instance across standalone calls and cleaning
it up at lifecycle boundaries (init, disconnect, toggleTestnet, etc.).

Reorder switchProvider() to check "already active" before validating
the providers map, so it returns a no-op success before init().
@abretonc7s abretonc7s changed the title fix(perps): harden toggleTestnet and depositWithConfirmation edge cases fix(perps): address bugbot findings on Core PR #7941 Feb 16, 2026
Add uuidv4() fallback for currentDepositId which TypeScript cannot
narrow inside the update() callback after the variable was hoisted
to let binding with string | undefined type.
@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 92.30769% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.08%. Comparing base (3ab7d90) to head (9704d4f).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
app/controllers/perps/PerpsController.ts 92.30% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #26110      +/-   ##
==========================================
- Coverage   81.19%   81.08%   -0.11%     
==========================================
  Files        4398     4399       +1     
  Lines      113804   113996     +192     
  Branches    24448    24492      +44     
==========================================
+ Hits        92404    92438      +34     
- Misses      15012    15138     +126     
- Partials     6388     6420      +32     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…cription

Register all 34 PerpsControllerActions via registerMethodActionHandlers()
so inter-controller communication works through the messenger in core.
Store the RemoteFeatureFlagController:stateChange subscription handle
and clean it up in disconnect() to prevent leaks.
Copy link

@cursor cursor bot left a comment

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.

… and account switch

- Remove feature-flag unsubscribe from disconnect() so geo-blocking and
  HIP-3 flag changes keep propagating after reconnect cycles
- Add deposit request lifecycle tracking for the deposit+order flow so
  requests transition from pending to completed/failed/cancelled
- Clear cached user data when switching to a non-EVM account group to
  prevent stale positions/orders from the previous EVM account
@github-actions github-actions bot added size-L and removed size-M labels Feb 16, 2026
@github-actions
Copy link
Contributor

🔍 Smart E2E Test Selection

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

E2E Test Selection:
The changes are focused on the PerpsController, a Phase 6 business logic controller that handles perpetuals trading functionality. Key changes include:

  1. Standalone Provider Caching: Added caching mechanism for HyperLiquidProvider to avoid WebSocket connection leaks during preload cycles. This is an internal optimization that affects how standalone queries (getPositions, getOpenOrders, getAccountState, getMarkets) are handled.

  2. Deposit Flow Improvements: Fixed deposit request lifecycle tracking for deposit+order flows, changed from never-resolving promise to returning transaction ID immediately, and added proper error handling to mark failed deposits.

  3. Network Toggle Rollback: Added proper rollback of isTestnet state when initialization fails during network toggle.

  4. Messenger Method Registration: Exposed controller methods via messenger using registerMethodActionHandlers (additive change).

  5. Account Change Handler Fix: Fixed logic to properly clear cached data when switching accounts.

These changes are isolated to the Perps feature domain and don't affect core Engine initialization or other controllers. The test file adds comprehensive coverage for the new functionality.

Tag selection rationale:

  • SmokePerps: Directly tests perpetuals trading functionality including Add Funds flow, balance verification, and deposits - all affected by these changes
  • SmokeWalletPlatform: Per tag description, Perps is a section inside the Trending tab, and changes to Perps views affect Trending
  • SmokeConfirmations: Per tag description, when selecting SmokePerps, also select SmokeConfirmations because Add Funds deposits are on-chain transactions that go through confirmation flows

Performance Test Selection:
The standalone provider caching changes could impact perps market loading and position management performance. The caching mechanism is designed to improve performance by avoiding repeated HyperLiquidProvider instantiation and WebSocket connection creation during preload cycles. Running @PerformancePreps will validate that these optimizations work correctly and don't introduce performance regressions in the perps flow (market loading, add funds flow, order execution).

View GitHub Actions results

@sonarqubecloud
Copy link

// Migrate old persisted data without accountAddress
this.#migrateRequestsIfNeeded();

this.messenger.registerMethodActionHandlers(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exposes PerpsController methods (placeOrder, cancelOrder, getPositions, etc.) via the messenger pattern so other controllers/services can call them through action handlers instead of needing a direct reference to the controller instance. It's the standard BaseController approach for inter-controller communication - keeps things decoupled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically we didnt need it outside of core, but when putting in core and want to share wiht other controllers we need it.

@abretonc7s abretonc7s added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit 670ee31 Feb 20, 2026
101 checks passed
@abretonc7s abretonc7s deleted the fix/perps/bugbotcore branch February 20, 2026 09:39
@github-actions github-actions bot locked and limited conversation to collaborators Feb 20, 2026
@metamaskbot metamaskbot added the release-7.68.0 Issue or pull request that will be included in release 7.68.0 label Feb 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.68.0 Issue or pull request that will be included in release 7.68.0 size-L team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants