Commit b5947ba
chore(runway): cherry-pick fix(perps): HL Unified-mode live balance — spotState ws + tradeable-balance + total-balance math cp-7.72.2 (#29259)
- fix(perps): HL Unified-mode live balance — spotState ws +
tradeable-balance + total-balance math cp-7.72.2 (#29226)
## **Description**
TAT-3016 follow-up covering the remaining HL balance gaps after the
initial spot-balance parity work.
**What's broken on main**
1. `AccountState.totalBalance` is never updated live after a limit order
is placed or cancelled — the HL spot clearinghouse state changes but
nothing reflects it in our streamed cache. The REST `#refreshSpotState`
path only runs on cold-start and standalone fetches, so the cached
snapshot goes stale on every on-chain event (local trade,
external-client trade, funding, liquidation, deposit, transfer).
2. `AccountState.availableToTradeBalance` doesn't exist — order-entry
surfaces read `availableBalance` (= HL `withdrawable`), which is always
`$0` on Unified-mode accounts whose collateral is held as spot USDC.
Users with tradeable balance see the app refuse to open an order.
3. `totalBalance` on the three account-state paths sums
`perps.accountValue + spot.total` without subtracting `spot.hold`. On
Unified/PM the held margin is reported in both fields, so pre-fix
`totalBalance` inflates by the margin amount whenever a limit order is
placed and deflates when cancelled — even though no wealth changed
hands.
**What this PR does**
- Subscribes to HL's `spotState` WebSocket channel alongside the
existing `webData2/3` user-data subscription. Handler updates
`#cachedSpotState`, bumps `#spotStateGeneration` to invalidate any
in-flight REST race, and re-runs `#aggregateAndNotifySubscribers` so UI
consumers see spot-folded totals within one network round-trip of the
change. REST fallback stays for cold-start and the standalone path.
- Adds `AccountState.availableToTradeBalance` as a first-class optional
field: `withdrawable + (spot.total − spot.hold)` for HL,
`availableBalance` trivial default for other providers. Order-entry
surfaces (`PerpsMarketBalanceActions`, `PerpsMarketDetailsView`,
`useDefaultPayWithTokenWhenNoPerpsBalance`, `usePerpsOrderForm`) read
`availableToTradeBalance ?? availableBalance`. Withdraw path
(`PerpsWithdrawView`) keeps reading `availableBalance` unchanged so the
withdraw row never leaks the spot fold.
- Subtracts `spot.hold` from the `totalBalance` sum in both the
single-DEX adapter and the aggregated fold helper. On Standard mode
`spot.hold = 0` so the subtraction is a no-op; on Unified/PM it cancels
the double-count. Result: `totalBalance` no longer ping-pongs on limit
place/cancel, matching HL web.
- Exposes `HyperLiquidProvider.getExchangeClient()` as a non-interface
escape hatch for agentic validation flows that drive HL mutations
directly.
- Adds a `perps-withdraw-available-balance-text` testID to anchor the
non-regression check that withdraw keeps rendering `availableBalance`.
- Adds the latest HL reference docs (`account-abstraction-modes.md`,
`portfolio-margin.md`, `margin-tiers.md`, updated `subscriptions.md` +
`margining.md`) so the code rationale can cite them.
**Follow-ups (not in this PR)**
- Rename `availableBalance → withdrawableBalance` (TAT-3047) — pure
rename against main, kept separate so OTA cherry-picks don't see symbol
drift.
- Agentic regression recipe exercising mode-flip + limit-cycle + REST
parity — tracked separately.
## **Changelog**
CHANGELOG entry: Fixed Perps balance not refreshing after trades,
funding, or transfers for HyperLiquid users, and corrected total balance
inflation on Unified-mode accounts.
## **Related issues**
Fixes:
[TAT-3016](https://consensyssoftware.atlassian.net/browse/TAT-3016)
Supersedes: #29150, #29217 (both closed).
## **Manual testing steps**
```gherkin
Feature: HL spotState live-balance refresh
Scenario: Spot-funded Unified account sees live balance
Given the Trading fixture (0x316B…01fA) is connected in Unified mode
And the Perps screen is open
Then AccountState.availableToTradeBalance reflects withdrawable + (spot.total − spot.hold)
And PerpsMarketBalanceActions 'Available' row shows the same value
And PerpsWithdrawView 'Available Perps balance' row shows $0 (withdrawable only)
And PerpsOrderView 'Pay with' row defaults to 'Perps balance'
Scenario: Limit order cycle leaves totalBalance stable
When the user places a limit order on BTC
Then AccountState.availableToTradeBalance drops by the reserved margin
And AccountState.totalBalance is unchanged
When the user cancels the limit
Then AccountState.availableToTradeBalance returns to baseline within 5s
And AccountState.totalBalance is still unchanged
Scenario: Screenshot parity with HL web
Then the total shown in the MetaMask Perps header matches app.hyperliquid.xyz (within $0.20)
And the order-form 'Available' value matches HL 'Available to trade'
```
## **Screenshots/Recordings**
### **Before**
<img width="422" height="865" alt="image"
src="https://github.com/user-attachments/assets/c83cba7e-c70d-442a-9fd3-db0feb7341a0"
/>
### **After**
<img width="412" height="881" alt="image"
src="https://github.com/user-attachments/assets/fd351457-1233-4105-9388-658c527f144e"
/>
## **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
- [ ] 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)).
Not required for external contributors.
#### 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-3016]:
https://consensyssoftware.atlassian.net/browse/TAT-3016?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[756b701](756b701)
[TAT-3016]:
https://consensyssoftware.atlassian.net/browse/TAT-3016?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Co-authored-by: abretonc7s <107169956+abretonc7s@users.noreply.github.com>1 parent 86438b3 commit b5947ba
20 files changed
Lines changed: 699 additions & 38 deletions
File tree
- app
- components/UI/Perps
- Views
- PerpsMarketDetailsView
- PerpsWithdrawView
- components/PerpsMarketBalanceActions
- hooks
- utils
- controllers/perps
- providers
- services
- types
- utils
- docs/perps/hyperliquid
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
326 | 332 | | |
327 | 333 | | |
328 | 334 | | |
| |||
Lines changed: 5 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
436 | 436 | | |
437 | 437 | | |
438 | 438 | | |
439 | | - | |
440 | | - | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
441 | 443 | | |
442 | 444 | | |
443 | 445 | | |
444 | | - | |
| 446 | + | |
445 | 447 | | |
446 | 448 | | |
447 | 449 | | |
| |||
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
406 | 406 | | |
407 | 407 | | |
408 | 408 | | |
409 | | - | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
410 | 414 | | |
411 | 415 | | |
412 | 416 | | |
| |||
Lines changed: 7 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
185 | | - | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
186 | 192 | | |
187 | 193 | | |
188 | 194 | | |
| |||
Lines changed: 6 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
48 | 46 | | |
49 | 47 | | |
50 | | - | |
| 48 | + | |
51 | 49 | | |
52 | 50 | | |
53 | 51 | | |
| |||
97 | 95 | | |
98 | 96 | | |
99 | 97 | | |
| 98 | + | |
100 | 99 | | |
101 | 100 | | |
102 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
95 | | - | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1077 | 1077 | | |
1078 | 1078 | | |
1079 | 1079 | | |
| 1080 | + | |
1080 | 1081 | | |
1081 | 1082 | | |
1082 | 1083 | | |
| |||
1120 | 1121 | | |
1121 | 1122 | | |
1122 | 1123 | | |
| 1124 | + | |
1123 | 1125 | | |
1124 | 1126 | | |
1125 | 1127 | | |
| |||
1158 | 1160 | | |
1159 | 1161 | | |
1160 | 1162 | | |
| 1163 | + | |
1161 | 1164 | | |
1162 | 1165 | | |
1163 | 1166 | | |
| |||
Lines changed: 17 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2148 | 2148 | | |
2149 | 2149 | | |
2150 | 2150 | | |
2151 | | - | |
| 2151 | + | |
2152 | 2152 | | |
2153 | 2153 | | |
2154 | 2154 | | |
| |||
3786 | 3786 | | |
3787 | 3787 | | |
3788 | 3788 | | |
3789 | | - | |
| 3789 | + | |
3790 | 3790 | | |
3791 | 3791 | | |
3792 | 3792 | | |
| |||
9821 | 9821 | | |
9822 | 9822 | | |
9823 | 9823 | | |
| 9824 | + | |
| 9825 | + | |
| 9826 | + | |
| 9827 | + | |
| 9828 | + | |
| 9829 | + | |
| 9830 | + | |
| 9831 | + | |
| 9832 | + | |
| 9833 | + | |
| 9834 | + | |
| 9835 | + | |
| 9836 | + | |
| 9837 | + | |
| 9838 | + | |
9824 | 9839 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
7772 | 7773 | | |
7773 | 7774 | | |
7774 | 7775 | | |
| 7776 | + | |
| 7777 | + | |
| 7778 | + | |
| 7779 | + | |
| 7780 | + | |
| 7781 | + | |
| 7782 | + | |
| 7783 | + | |
| 7784 | + | |
| 7785 | + | |
| 7786 | + | |
| 7787 | + | |
| 7788 | + | |
7775 | 7789 | | |
7776 | 7790 | | |
7777 | 7791 | | |
| |||
Lines changed: 121 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
283 | 283 | | |
284 | 284 | | |
285 | 285 | | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
286 | 292 | | |
287 | 293 | | |
288 | 294 | | |
| |||
3684 | 3690 | | |
3685 | 3691 | | |
3686 | 3692 | | |
| 3693 | + | |
| 3694 | + | |
| 3695 | + | |
| 3696 | + | |
| 3697 | + | |
| 3698 | + | |
| 3699 | + | |
| 3700 | + | |
| 3701 | + | |
| 3702 | + | |
| 3703 | + | |
| 3704 | + | |
| 3705 | + | |
| 3706 | + | |
| 3707 | + | |
| 3708 | + | |
| 3709 | + | |
| 3710 | + | |
| 3711 | + | |
| 3712 | + | |
| 3713 | + | |
| 3714 | + | |
| 3715 | + | |
| 3716 | + | |
| 3717 | + | |
| 3718 | + | |
| 3719 | + | |
| 3720 | + | |
| 3721 | + | |
| 3722 | + | |
| 3723 | + | |
| 3724 | + | |
| 3725 | + | |
| 3726 | + | |
| 3727 | + | |
| 3728 | + | |
| 3729 | + | |
| 3730 | + | |
| 3731 | + | |
| 3732 | + | |
| 3733 | + | |
| 3734 | + | |
| 3735 | + | |
| 3736 | + | |
| 3737 | + | |
| 3738 | + | |
| 3739 | + | |
| 3740 | + | |
| 3741 | + | |
| 3742 | + | |
| 3743 | + | |
| 3744 | + | |
| 3745 | + | |
| 3746 | + | |
| 3747 | + | |
| 3748 | + | |
| 3749 | + | |
| 3750 | + | |
| 3751 | + | |
| 3752 | + | |
| 3753 | + | |
| 3754 | + | |
| 3755 | + | |
| 3756 | + | |
| 3757 | + | |
| 3758 | + | |
| 3759 | + | |
| 3760 | + | |
| 3761 | + | |
| 3762 | + | |
| 3763 | + | |
| 3764 | + | |
| 3765 | + | |
| 3766 | + | |
| 3767 | + | |
| 3768 | + | |
| 3769 | + | |
| 3770 | + | |
| 3771 | + | |
| 3772 | + | |
| 3773 | + | |
| 3774 | + | |
| 3775 | + | |
| 3776 | + | |
| 3777 | + | |
| 3778 | + | |
| 3779 | + | |
| 3780 | + | |
| 3781 | + | |
| 3782 | + | |
| 3783 | + | |
| 3784 | + | |
| 3785 | + | |
| 3786 | + | |
| 3787 | + | |
| 3788 | + | |
| 3789 | + | |
| 3790 | + | |
| 3791 | + | |
| 3792 | + | |
| 3793 | + | |
| 3794 | + | |
| 3795 | + | |
| 3796 | + | |
| 3797 | + | |
| 3798 | + | |
| 3799 | + | |
| 3800 | + | |
| 3801 | + | |
| 3802 | + | |
| 3803 | + | |
| 3804 | + | |
| 3805 | + | |
| 3806 | + | |
| 3807 | + | |
3687 | 3808 | | |
3688 | 3809 | | |
3689 | 3810 | | |
| |||
0 commit comments