chore(runway): cherry-pick feat(perps): force unified account#29714
Merged
Conversation
## **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>
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. |
Contributor
🔍 Smart E2E Test Selection⏭️ Smart E2E selection skipped - PR targets a release branch (release/*) All E2E tests pre-selected. |
|
Contributor
|
Force merge this PR as the audit issue on release branch will be fixed by: #29719 |
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.



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 viaagentSetAbstraction({ abstraction: 'u' })— no signing promptdexAbstraction→ one-time EIP-712 prompt viauserSetAbstraction({ user, abstraction: 'unifiedAccount' })— agent-key path is blocked byHL for this transition
unifiedAccount→ no-op, cached immediatelyKey details:
agentEnableDexAbstraction/userDexAbstractionwith
agentSetAbstraction/userSetAbstraction/userAbstraction#ensureReady()) so users are set upbefore trading
TradingReadinessCacheprevents repeated prompts (critical forhardware/QR wallets);
KEYRING_LOCKEDskips the cache so it retries onunlock
provider instances
Perp Account Setupevent tracks mode distributionalready_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.withdrawableis $0 — pre-fix the withdraw screenshowed $0 max with the button disabled, and the confirm-flow alert
blocked submission.
accountUtils.addSpotBalanceToAccountStatefolds free spot USDC intoavailableToTradeBalancefor Unified / Portfolio Margin;dexAbstraction/ Standard keep spot separate (fold gated on resolvedabstraction mode)
HyperLiquidSubscriptionService.invalidateUserAbstractionCache(addr)evicts stale pre-migration mode and re-aggregates immediately. Called by
HyperLiquidProviderafter both successful migration paths so theWS-driven aggregator doesn't serve a $0 balance for ~60s after migration
completes.
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
Live validation evidence
Validated on dev1 mainnet (
0x8dc6…9003) in the exact bug-class state:unifiedAccount/ perpswithdrawable: $0 / spot USDC free:$26.41
availableBalance= $0 /availableToTradeBalance= $26.41enabled (pre-fix would show $0 / disabled)
Screenshots/Recordings
Before
After
Pre-merge author checklist
Docs and MetaMask Mobile
Coding
Standards.
if applicable
guidelines).
Performance checks (if applicable)
performance metrics
Pre-merge reviewer checklist
app, test code being changed).
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 globalin-flight/cached gating, retry semantics, and new
Perp Account Setupanalytics.
Updates withdraw, confirmation, and pay-with flows to prefer
availableToTradeBalance ?? availableBalance, and changes spot→perpsfolding 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