Skip to content

chore(runway): cherry-pick feat(perps): force unified account#29714

Merged
chloeYue merged 1 commit into
release/7.76.0from
runway-cherry-pick-7.76.0-1777967537
May 5, 2026
Merged

chore(runway): cherry-pick feat(perps): force unified account#29714
chloeYue merged 1 commit into
release/7.76.0from
runway-cherry-pick-7.76.0-1777967537

Conversation

@runway-github

@runway-github runway-github Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Description

HyperLiquid is deprecating DEX Abstraction mode (~May 9). This PR forces
every Perps user onto Unified Account mode on app open and fixes the
withdraw + balance-display flows that were broken in the target state.

1. Forced migration to Unified Account

Migration paths by current abstraction mode:

  • default / disabled → silently migrated via agentSetAbstraction({ abstraction: 'u' }) — no signing prompt
  • dexAbstraction → one-time EIP-712 prompt via userSetAbstraction({ user, abstraction: 'unifiedAccount' }) — agent-key path is blocked by
    HL for this transition
  • unifiedAccount → no-op, cached immediately

Key details:

  • Replaces deprecated agentEnableDexAbstraction / userDexAbstraction
    with agentSetAbstraction / userSetAbstraction / userAbstraction
  • Runs on perps section open (#ensureReady()) so users are set up
    before trading
  • TradingReadinessCache prevents repeated prompts (critical for
    hardware/QR wallets); KEYRING_LOCKED skips the cache so it retries on
    unlock
  • In-flight deduplication blocks concurrent signing attempts across
    provider instances
  • Segment analytics: Perp Account Setup event tracks mode distribution
  • outcome (already_enabled / migration_required / success /
    failed)

2. Withdraw + balance display fix (folded in from #29537)

In Unified mode, USDC collateral lives in the spot clearinghouse, so
clearinghouseState.withdrawable is $0 — pre-fix the withdraw screen
showed $0 max with the button disabled, and the confirm-flow alert
blocked submission.

  • accountUtils.addSpotBalanceToAccountState folds free spot USDC into
    availableToTradeBalance for Unified / Portfolio Margin;
    dexAbstraction / Standard keep spot separate (fold gated on resolved
    abstraction mode)
  • HyperLiquidSubscriptionService.invalidateUserAbstractionCache(addr)
    evicts stale pre-migration mode and re-aggregates immediately. Called by
    HyperLiquidProvider after both successful migration paths so the
    WS-driven aggregator doesn't serve a $0 balance for ~60s after migration
    completes.
  • Withdraw screen, withdraw validation, confirm-flow
    insufficient-balance alert, and percentage buttons all read
    availableToTradeBalance ?? availableBalance — fallback keeps Standard
    / legacy callers correct.

Changelog

CHANGELOG entry: Fixed Hyperliquid withdraw showing $0 and being blocked
for users on Unified Account mode.

Related issues

Fixes: TAT-3112 (Unified Account migration), withdrawal break tracked in
TAT-3047

Manual testing steps

Feature: Unified Account migration + withdraw

  Scenario: First-time migration (default/disabled mode)
    Given the user has never used Perps
    When they open the Perps section
    Then migration runs silently (no prompt)
    And HIP-3 markets are visible

  Scenario: dexAbstraction → unifiedAccount migration
    Given the user has DEX Abstraction enabled
    When they open the Perps section
    Then a one-time EIP-712 signing prompt appears
    When they sign
    Then HIP-3 markets are visible and trades succeed
    And reopening Perps does not prompt again

  Scenario: Unified Account user withdraws spot-funded balance
    Given the user is in Unified Mode with $0 perps withdrawable and >$0 spot USDC
    When they open the Withdraw screen
    Then "Available Perps balance" shows the unified value (perps + free spot USDC)
    And Max enables and submission proceeds via withdraw3
    And spot USDC drops by amount + fee

Live validation evidence

Validated on dev1 mainnet (0x8dc6…9003) in the exact bug-class state:

  • HL mode: unifiedAccount / perps withdrawable: $0 / spot USDC free:
    $26.41
  • App: availableBalance = $0 / availableToTradeBalance = $26.41
  • Withdraw screen renders "Available Perps balance: $26.41" + Max
    enabled (pre-fix would show $0 / disabled)

Screenshots/Recordings

Before

After

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
  • I've tested with a power user scenario
  • I've instrumented key operations with Sentry traces for production
    performance metrics

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.

Note

High Risk
High risk because it changes Perps account-mode migration/signing flow
(including hardware-wallet behavior) and alters withdraw/payment balance
calculations that gate user funds and transaction validation.

Overview
Forces Perps users onto HyperLiquid Unified Account by replacing
deprecated DEX-abstraction checks/calls with userAbstraction +
agentSetAbstraction/userSetAbstraction, adding global
in-flight/cached gating, retry semantics, and new Perp Account Setup
analytics.

Updates withdraw, confirmation, and pay-with flows to prefer
availableToTradeBalance ?? availableBalance, and changes spot→perps
folding to be mode-gated (fail-closed when abstraction mode is
unknown) so Unified/Portfolio Margin users see spendable USDC while
Standard/dexAbstraction users don’t over-report withdrawable funds.

Renames cache-clearing APIs from DEX abstraction to Unified Account,
adds hardware-wallet detection to defer user-sign prompts on browse, and
expands tests/docs to cover unified-mode folding, migration paths, and
race conditions in spot/account aggregation.

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


Co-authored-by: geositta matthew.denton@consensys.net
Co-authored-by: Nick Gambino nicholas.gambino@consensys.net
Co-authored-by: Arthur Breton arthur.breton@consensys.net
Co-authored-by: abretonc7s 107169956+abretonc7s@users.noreply.github.com cc44460

## **Description**

HyperLiquid is deprecating DEX Abstraction mode (~May 9). This PR forces
every Perps user onto **Unified Account** mode on app open and fixes the
withdraw + balance-display flows that were broken in the target state.

### 1. Forced migration to Unified Account

Migration paths by current abstraction mode:
- `default` / `disabled` → silently migrated via `agentSetAbstraction({
abstraction: 'u' })` — no signing prompt
- `dexAbstraction` → one-time EIP-712 prompt via `userSetAbstraction({
user, abstraction: 'unifiedAccount' })` — agent-key path is blocked by
HL for this transition
- `unifiedAccount` → no-op, cached immediately

Key details:
- Replaces deprecated `agentEnableDexAbstraction` / `userDexAbstraction`
with `agentSetAbstraction` / `userSetAbstraction` / `userAbstraction`
- Runs on perps section open (`#ensureReady()`) so users are set up
before trading
- `TradingReadinessCache` prevents repeated prompts (critical for
hardware/QR wallets); `KEYRING_LOCKED` skips the cache so it retries on
unlock
- In-flight deduplication blocks concurrent signing attempts across
provider instances
- Segment analytics: `Perp Account Setup` event tracks mode distribution
+ outcome (`already_enabled` / `migration_required` / `success` /
`failed`)

### 2. Withdraw + balance display fix (folded in from #29537)

In Unified mode, USDC collateral lives in the spot clearinghouse, so
`clearinghouseState.withdrawable` is $0 — pre-fix the withdraw screen
showed $0 max with the button disabled, and the confirm-flow alert
blocked submission.

- `accountUtils.addSpotBalanceToAccountState` folds free spot USDC into
`availableToTradeBalance` for Unified / Portfolio Margin;
`dexAbstraction` / Standard keep spot separate (fold gated on resolved
abstraction mode)
- `HyperLiquidSubscriptionService.invalidateUserAbstractionCache(addr)`
evicts stale pre-migration mode and re-aggregates immediately. Called by
`HyperLiquidProvider` after both successful migration paths so the
WS-driven aggregator doesn't serve a $0 balance for ~60s after migration
completes.
- Withdraw screen, withdraw validation, confirm-flow
insufficient-balance alert, and percentage buttons all read
`availableToTradeBalance ?? availableBalance` — fallback keeps Standard
/ legacy callers correct.

## **Changelog**

CHANGELOG entry: Fixed Hyperliquid withdraw showing $0 and being blocked
for users on Unified Account mode.

## **Related issues**

Fixes: TAT-3112 (Unified Account migration), withdrawal break tracked in
[TAT-3047](https://consensyssoftware.atlassian.net/browse/TAT-3047)

## **Manual testing steps**

```gherkin
Feature: Unified Account migration + withdraw

  Scenario: First-time migration (default/disabled mode)
    Given the user has never used Perps
    When they open the Perps section
    Then migration runs silently (no prompt)
    And HIP-3 markets are visible

  Scenario: dexAbstraction → unifiedAccount migration
    Given the user has DEX Abstraction enabled
    When they open the Perps section
    Then a one-time EIP-712 signing prompt appears
    When they sign
    Then HIP-3 markets are visible and trades succeed
    And reopening Perps does not prompt again

  Scenario: Unified Account user withdraws spot-funded balance
    Given the user is in Unified Mode with $0 perps withdrawable and >$0 spot USDC
    When they open the Withdraw screen
    Then "Available Perps balance" shows the unified value (perps + free spot USDC)
    And Max enables and submission proceeds via withdraw3
    And spot USDC drops by amount + fee
```

### Live validation evidence

Validated on dev1 mainnet (`0x8dc6…9003`) in the exact bug-class state:
- HL mode: `unifiedAccount` / perps `withdrawable`: $0 / spot USDC free:
$26.41
- App: `availableBalance` = $0 / `availableToTradeBalance` = $26.41
- Withdraw screen renders **"Available Perps balance: $26.41"** + Max
enabled (pre-fix would show $0 / disabled)

## **Screenshots/Recordings**

### **Before**

<!-- $0 max + disabled Max button on dev1 mainnet pre-fix -->

### **After**

<!-- $26.41 max + Max enabled + successful withdraw on dev1 mainnet -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [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](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).

#### Performance checks (if applicable)

- [ ] I've tested on Android
- [ ] I've tested with a power user scenario
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics

## **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.


[TAT-3047]:
https://consensyssoftware.atlassian.net/browse/TAT-3047?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> High risk because it changes Perps account-mode migration/signing flow
(including hardware-wallet behavior) and alters withdraw/payment balance
calculations that gate user funds and transaction validation.
> 
> **Overview**
> Forces Perps users onto HyperLiquid **Unified Account** by replacing
deprecated DEX-abstraction checks/calls with `userAbstraction` +
`agentSetAbstraction`/`userSetAbstraction`, adding global
in-flight/cached gating, retry semantics, and new `Perp Account Setup`
analytics.
> 
> Updates withdraw, confirmation, and pay-with flows to prefer
`availableToTradeBalance ?? availableBalance`, and changes spot→perps
folding to be **mode-gated** (fail-closed when abstraction mode is
unknown) so Unified/Portfolio Margin users see spendable USDC while
Standard/dexAbstraction users don’t over-report withdrawable funds.
> 
> Renames cache-clearing APIs from DEX abstraction to Unified Account,
adds hardware-wallet detection to defer user-sign prompts on browse, and
expands tests/docs to cover unified-mode folding, migration paths, and
race conditions in spot/account aggregation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e5495f9. 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: geositta <matthew.denton@consensys.net>
Co-authored-by: Nick Gambino <nicholas.gambino@consensys.net>
Co-authored-by: Arthur Breton <arthur.breton@consensys.net>
Co-authored-by: abretonc7s <107169956+abretonc7s@users.noreply.github.com>
@runway-github runway-github Bot requested review from a team as code owners May 5, 2026 07:52
@metamaskbotv2 metamaskbotv2 Bot added the team-bots Bot team (for MetaMask Bot, Runway Bot, etc.) label May 5, 2026
@github-actions

github-actions Bot commented May 5, 2026

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.

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - PR targets a release branch (release/*)

All E2E tests pre-selected.

View GitHub Actions results

@sonarqubecloud

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

@chloeYue chloeYue 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.

LGTM

@chloeYue

chloeYue commented May 5, 2026

Copy link
Copy Markdown
Contributor

Force merge this PR as the audit issue on release branch will be fixed by: #29719

@chloeYue chloeYue merged commit 9bd7c1e into release/7.76.0 May 5, 2026
169 of 214 checks passed
@chloeYue chloeYue deleted the runway-cherry-pick-7.76.0-1777967537 branch May 5, 2026 09:06
@github-actions github-actions Bot locked and limited conversation to collaborators May 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

size-XL team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants