Skip to content

fix(perps): default spot-funded order entry to perps balance cp-7.72.2#29150

Closed
geositta wants to merge 17 commits into
mainfrom
perps/fix-avail-balance-order-entry
Closed

fix(perps): default spot-funded order entry to perps balance cp-7.72.2#29150
geositta wants to merge 17 commits into
mainfrom
perps/fix-avail-balance-order-entry

Conversation

@geositta
Copy link
Copy Markdown
Contributor

@geositta geositta commented Apr 22, 2026

Description

HyperLiquid users in Unified Account Mode whose collateral is spot USDC (withdrawable = 0, spot USDC > 0) can't place orders in MetaMask Mobile even though Phantom and HL web correctly treat the same account as tradeable. Incident: TAT-3016.

What the fix does

  • Introduces AccountState.availableToTradeBalance?: string = withdrawable + getSpotBalance(spotState) (USDC + USDH via SPOT_COLLATERAL_COINS allowlist, USDC-only today).
  • Gates order-entry UI surfaces on availableToTradeBalance ?? availableBalance (market-details balance row, Long/Short CTA vs Add-Funds CTA, order-form submit gate).
  • Preserves availableBalance = withdrawable on the withdraw path. Critical non-regression — users must never see the spot fold offered as withdrawable.
  • HyperLiquid-only surface. MYX providers receive the trivial availableToTradeBalance = availableBalance field.

Matches HyperLiquid's documented unified account / portfolio margin integration pattern: reconcile clearinghouseState with spotClearinghouseState, keep availableBalance as raw withdrawable, use synthesized availableToTradeBalance for order-entry funding decisions. Also matches observed behaviour in Phantom.

Product assumption (explicit)

Spot USDC is treated as interchangeable with Perps trading collateral for the markets we currently surface.

Correct for the current USDC-only market surface and acceptable for current pre-alpha scope. Revisit when HIP-3 / non-USDC markets roll out — tracked as TAT-3047.

Shape of the change

  • app/controllers/perps/types/index.ts — new optional availableToTradeBalance field on AccountState.
  • app/controllers/perps/utils/accountUtils.tsaddSpotBalanceToAccountState now bumps both totalBalance and availableToTradeBalance using getSpotBalance (USDC + USDH allowlist). Wrapper helpers deleted.
  • app/controllers/perps/utils/hyperLiquidAdapter.ts, providers/HyperLiquidProvider.ts, services/HyperLiquidSubscriptionService.ts — call sites updated to the folded helper; WebSocket #hashAccountState includes the new field so subscribers re-notify on change.
  • app/components/UI/Perps/hooks/useDefaultPayWithTokenWhenNoPerpsBalance.ts — balance gate reads availableToTradeBalance ?? availableBalance. One internal helper + three tiny exports (useHasNativeTradeablePerpsBalance, usePreferredFallbackPayTokenCandidate, arePaymentTokensEqual) consumed by useInitPerpsPaymentToken.
  • app/components/UI/Perps/Views/PerpsOrderView/useInitPerpsPaymentToken.ts — simple inference: clear saved token iff hasNativeTradeablePerpsBalance && arePaymentTokensEqual(savedToken, fallbackCandidate); else restore. No state machine, no selectedPaymentTokenSource tag.
  • app/components/UI/Perps/hooks/usePerpsOrderForm.ts, Views/PerpsOrderView/PerpsOrderView.tsx, hooks/usePerpsPaymentTokens.ts, hooks/usePerpsBalanceTokenFilter.ts, components/PerpsMarketBalanceActions/PerpsMarketBalanceActions.tsx, Views/PerpsMarketDetailsView/PerpsMarketDetailsView.tsx — consume the fallback pattern.
  • app/components/UI/Perps/Views/PerpsWithdrawView/PerpsWithdrawView.tsx + Perps.testIds.ts — adds perps-withdraw-available-balance-text testID on the displayed balance so automation can assert the withdraw path does not leak the fold.
  • app/controllers/perps/providers/HyperLiquidProvider.ts — new getExchangeClient() public escape hatch used by admin/test flows (e.g. driving userSetAbstraction/usdClassTransfer from the agentic recipe). Not on the PerpsProvider interface.

Validation harness

scripts/perps/agentic/teams/perps/flows/:

  • hl-balance-validation — captures AccountState + PerpsMarketListView balance text + PerpsWithdrawView balance text + PerpsOrderView Pay-with symbol with deterministic wait_for gates. Asserts fold invariant and no-fold-leak on withdraw.
  • hl-provision-fixture — wraps exchangeClient.userSetAbstraction and exchangeClient.usdClassTransfer so recipes can cycle HL abstraction modes programmatically.

Plus hl-fixture-state eval ref in scripts/perps/agentic/teams/perps/evals.json exposing the shared balance snapshot. Every timing gate is wait_for polling a deterministic condition — no arbitrary wait: Nms.

Out of scope (deliberate)

  • No contract reshape (availableBalance → spendableBalance / withdrawableBalance) — TAT-3047.
  • No withdraw-path logic edits. availableBalance semantics preserved.
  • No MYX logic beyond the trivial availableToTradeBalance = availableBalance field.
  • No upstream @metamask/perps-controller changes.
  • Mode-aware fold gating (honour Unified vs Standard on the mobile side) — TAT-3047.
  • Cross-account selectedPaymentToken reset — pre-existing bug discovered during validation, filed as TAT-3050.

Known behavioural change

totalBalance on HyperLiquid AccountState now reflects withdrawable + allowlisted spot (USDC + USDH) only, not the sum of all spot coin totals. Non-stable spot holdings (e.g. HYPE) no longer contribute to the displayed total on perps surfaces. This matches the fold allowlist SPOT_COLLATERAL_COINS. Worth confirming with product if portfolio totals were relied on to include non-collateral spot.

Changelog

CHANGELOG entry: Fixed Perps order entry for HyperLiquid Unified Mode users whose collateral is spot USDC.

Related issues

Fixes: TAT-3016

Context:

  • Prior merged fix (complementary): #29110
  • Review strip PR (layered cleanup): #29171 — merged into this branch
  • Follow-up (contract reshape): TAT-3047
  • Follow-up (pay-with leak): TAT-3050

Manual testing steps

The useful validation is automated. Run bash scripts/perps/agentic/validate-recipe.sh <artifacts-dir> with the recipe below against the shared Trading account (currently in the exact "Unified Mode + spot-only" state that triggered the incident).

Feature: TAT-3016 HL abstraction cycle

  Scenario: Spot-funded Trading account cycled through Unified / Standard / Unified
    Given the Trading account (0x316B…01fA) is in Unified Mode with spot USDC collateral
    And all open positions have been closed

    When the recipe captures AccountState + PerpsMarketListView balance + PerpsWithdrawView balance + PerpsOrderView Pay-with
    And flips HL abstraction to Standard via userSetAbstraction
    And re-captures
    And flips back to Unified
    And captures once more

    Then availableToTradeBalance >= availableBalance in every phase
    And PerpsMarketListView shows the fold (≈ $100.76) in every phase
    And PerpsWithdrawView shows "Available Perps balance: $0" in every phase
    And PerpsOrderView Pay-with row shows "Perps balance" in every phase
    And teardown leaves the account in Unified Mode

Observed values on the shared Trading account (last run: 105/105 PASS):

Phase availableBalance availableToTradeBalance Market-list balance Withdraw balance Order-form Pay-with
Initial (Unified) $0.00 $100.75 $100.76 Available Perps balance: $0 Perps balance
After flip → Standard $0.00 $100.75 $100.76 Available Perps balance: $0 Perps balance
Restored (Unified) $0.00 $100.75 $100.76 Available Perps balance: $0 Perps balance

Identical balances across Unified / Standard flips are intentional per the product assumption above.

Screenshots/Recordings

Before

User cannot place orders on HL Unified Mode account with spot USDC collateral — the balance card displays $0 and the order form auto-selects an external USDC pay token with submit disabled.

After

Three-phase cycle evidence. Each phase captures market-list balance (shows fold), withdraw balance (shows withdrawable-only), and order-form Pay-with (shows Perps balance by default).

Market list Withdraw Order form (Pay-with)
Initial (Unified) Initial market list Initial withdraw Initial order form
After flip → Standard Standard market list Standard withdraw Standard order form
Restored (Unified) Restored market list Restored withdraw Restored order form

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

Medium Risk
Changes how perps buying power is computed and propagated (new availableToTradeBalance, spot hold handling, subscription hashing), which can affect order-entry gating and displayed balances across providers and UI surfaces.

Overview
Fixes HyperLiquid unified-mode accounts where withdrawable is 0 but spot USDC collateral exists by introducing AccountState.availableToTradeBalance and using it (fallbacking to availableBalance) to gate order-entry flows.

Controllers/adapters now compute tradeable balance by adding supported spot collateral (USDC) minus hold to perps withdrawable, aggregate it across DEX subaccounts, and include it in subscription hashing so UI updates when it changes; MYX returns a trivial availableToTradeBalance = availableBalance. UI hooks/views (market details CTA, order form validation/max, payment token lists/filtering, balance header) now prefer this tradeable balance while keeping withdraw flows pinned to availableBalance, with additional test coverage and a new withdraw-screen testID to assert the invariant.

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

@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 Apr 22, 2026
@geositta geositta marked this pull request as ready for review April 22, 2026 05:06
@geositta geositta requested a review from a team as a code owner April 22, 2026 05:06
@geositta geositta force-pushed the perps/fix-avail-balance-order-entry branch from 8f716de to 24a94e3 Compare April 22, 2026 05:07
@github-actions github-actions Bot added the risk-medium Moderate testing recommended · Possible bug introduction risk label Apr 22, 2026
Comment thread app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx Outdated
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 22, 2026
Comment thread app/components/UI/Perps/hooks/usePerpsSavePendingConfig.ts Outdated
Comment thread app/components/UI/Perps/hooks/useDefaultPayWithTokenWhenNoPerpsBalance.ts Outdated
The generated action types file was stale after adding the
selectedPaymentTokenSource parameter, causing yarn lint to fail.
Comment thread app/components/UI/Perps/hooks/usePerpsSavePendingConfig.test.ts Outdated
…cycle validation (#29171)

## **Description**

Small cleanup on top of Matt's TAT-3016 hotfix, plus an agentic
validation harness that proves the fix contract holds end-to-end.

Matt's hotfix surfaces `availableToTradeBalance = withdrawable + spot
USDC` for order-entry while keeping `availableBalance = withdrawable`
for the withdraw path.
[#29150](#29150)
introduced that change but carried a lot of incidental churn — a state
machine for payment-token sources, a wrapper helper that only added
noise, and a 215-line refactor of
`useDefaultPayWithTokenWhenNoPerpsBalance`. None of those were part of
the fix contract. This PR strips them and inlines a simpler inference:
clear the saved payment token only when the user now has native buying
power **and** the saved token matches the auto-fallback candidate.

Net production-code delta vs the base hotfix: **−182 lines**.

### Validation harness

One recipe drives the shared Trading account (currently in the exact
"Unified Mode + spot-only" state that broke orders) through three
phases: capture initial state, flip HL abstraction to Standard via
`userSetAbstraction`, capture, flip back to Unified, capture. Setup
closes open positions (HL rejects abstraction flips otherwise); teardown
restores Unified.

Three small additions make this composable for future perps PRs:

- A `testID` on `PerpsWithdrawView`'s available-balance text so
automation can read what users see.
- `HyperLiquidProvider.getExchangeClient()` — narrow escape hatch that
lets admin/test flows drive any SDK action without growing a controller
method per operation. Not on the `PerpsProvider` interface.
- Two reusable flows: `hl-balance-validation` (captures AccountState +
PerpsMarketListView balance + PerpsWithdrawView balance, asserts the
fold invariant and no-fold-leak on withdraw) and `hl-provision-fixture`
(wraps `userSetAbstraction` + `usdClassTransfer`).

Every timing gate in the recipe is a `wait_for` polling a deterministic
condition. No arbitrary `wait: Nms`.

### Out of scope (deliberate)

- No contract reshape (`availableBalance → spendableBalance` /
`withdrawableBalance`) — that's
[TAT-3047](https://consensyssoftware.atlassian.net/browse/TAT-3047).
- No withdraw-path logic edits. `availableBalance` semantics preserved.
- No MYX logic beyond the trivial `availableToTradeBalance =
availableBalance` field.
- No upstream `@metamask/perps-controller` changes.
- Mode-aware fold gating (honour Unified vs Standard on the mobile side)
— also
[TAT-3047](https://consensyssoftware.atlassian.net/browse/TAT-3047).

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:
[TAT-3016](https://consensyssoftware.atlassian.net/browse/TAT-3016)

Context:
- Supersedes:
[#29150](#29150) (credit
to @geositta)
- Prior merged fix (complementary):
[#29110](#29110)
- Follow-up:
[TAT-3047](https://consensyssoftware.atlassian.net/browse/TAT-3047)

## **Manual testing steps**

The useful path is automated. A reviewer who wants to reproduce can run
the recipe against the shared Trading account:

```gherkin
Feature: TAT-3016 HL abstraction cycle

  Scenario: Spot-funded Trading account cycled through Unified / Standard / Unified
    Given the Trading account (0x316B…01fA) is in Unified Mode with spot USDC collateral
    And all open positions have been closed

    When the recipe captures AccountState + PerpsMarketListView balance + PerpsWithdrawView balance
    And flips HL abstraction to Standard via userSetAbstraction
    And re-captures
    And flips back to Unified
    And captures once more

    Then availableToTradeBalance >= availableBalance in every phase
    And PerpsMarketListView shows the fold (≈ $100.76) in every phase
    And PerpsWithdrawView shows "Available Perps balance: $0" in every phase
    And teardown leaves the account in Unified Mode
```

Observed values on the shared Trading account:

| Phase | `availableBalance` | `availableToTradeBalance` | Market-list
balance | Withdraw balance |
|---|---|---|---|---|
| Initial (Unified) | $0.00 | $100.75 | **$100.76** | **Available Perps
balance: $0** |
| After flip → Standard | $0.00 | $100.75 | **$100.76** | **Available
Perps balance: $0** |
| Restored (Unified) | $0.00 | $100.75 | **$100.76** | **Available Perps
balance: $0** |

Identical balances across Unified / Standard flips are **intentional**
per #29150's product assumption: "spot USDC is treated as
interchangeable with Perps trading collateral for the markets we
currently surface." HL's HIP-3 / non-USDC markets are deferred to
TAT-3047.

## **Screenshots/Recordings**

### **Before**

This PR layers on top of Matt's already-landing hotfix; the user-visible
behavior does not change. The harness below demonstrates the hotfix
contract is preserved across the cleanup.

### **After**

Three-phase cycle evidence. Two screenshots per phase — market-list
(shows fold) and withdraw (shows withdrawable-only).

| | Market list | Withdraw |
|---|---|---|
| **Initial (Unified)** | ![Initial market
list](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204311_tat3016-initial-unified-market-list.png)
| ![Initial
withdraw](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204316_tat3016-initial-unified-withdraw.png)
|
| **After flip → Standard** | ![Standard market
list](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204325_tat3016-after-flip-standard-market-list.png)
| ![Standard
withdraw](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204330_tat3016-after-flip-standard-withdraw.png)
|
| **Restored (Unified)** | ![Restored market
list](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204339_tat3016-restored-unified-market-list.png)
| ![Restored
withdraw](https://raw.githubusercontent.com/abretonc7s/mm-mobile-farm-artifacts/main/fixes/29171/cycle/2026-04-22_204344_tat3016-restored-unified-withdraw.png)
|

## **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)).
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-3047]:
https://consensyssoftware.atlassian.net/browse/TAT-3047?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
@github-actions github-actions Bot added size-XL and removed size-L labels Apr 22, 2026
Comment thread app/components/UI/Perps/Views/PerpsOrderView/useInitPerpsPaymentToken.ts Outdated
Comment thread app/components/UI/Perps/hooks/useDefaultPayWithTokenWhenNoPerpsBalance.ts Outdated
@abretonc7s abretonc7s changed the title fix(perps): default spot-funded order entry to perps balance fix(perps): default spot-funded order entry to perps balance cp-7.72.2 Apr 22, 2026
Copy link
Copy Markdown
Contributor

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

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 7a22ab7. Configure here.

Comment thread app/controllers/perps/providers/MYXProvider.test.ts
@abretonc7s abretonc7s enabled auto-merge April 22, 2026 15:15
…d helpers

Consolidate computePreferredFallbackPayTokenCandidate, arePaymentTokensEqual,
useHasNativeTradeablePerpsBalance, and usePreferredFallbackPayTokenCandidate
directly into useDefaultPayWithTokenWhenNoPerpsBalance. Simplify
useInitPerpsPaymentToken by removing the stale auto-fallback clearing path
and guarding re-application with the appliedPendingTokenRef.
@github-actions github-actions Bot added size-L and removed size-XL labels Apr 22, 2026
@github-actions github-actions Bot added risk:high AI analysis: high risk and removed risk:medium AI analysis: medium risk labels Apr 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

AI PR Analysis

🚫 Merge safe: false | 🟠 Risk: high

Merge decision: AI analysis did not complete — manual review required before merging.

AI analysis did not complete. Manual review recommended.

View run

@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: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:
The PR introduces a new availableToTradeBalance field to the Perps account state system across providers, adapters, subscription services, and UI hooks/components. This is a focused but impactful change to the Perps feature:

  1. SmokePerps (primary): The changes directly affect the Add Funds flow, balance verification, deposit flows, and balance display in the Perps interface. The availableToTradeBalance field is now used for:

    • Order form initialization (max amount, balance percentage calculations)
    • Payment token selection (whether to auto-select a custom token when perps balance is low)
    • Balance display in PerpsMarketBalanceActions
    • "Add Funds" CTA visibility in PerpsMarketDetailsView
    • Token filter for payment options
      All SmokePerps test scenarios (Add Funds flow, balance verification, balance updates) are directly impacted.
  2. SmokeWalletPlatform (required per SmokePerps tag description): Perps is a section inside the Trending tab. Changes to Perps views (PerpsMarketDetailsView, PerpsWithdrawView, PerpsMarketBalanceActions) affect Trending. Per the tag description: "When selecting SmokePerps, also select SmokeWalletPlatform (Trending section)."

  3. SmokeConfirmations (required per SmokePerps tag description): The Add Funds flow involves on-chain transactions (deposits). Per the tag description: "When selecting SmokePerps, also select SmokeConfirmations (Add Funds deposits are on-chain transactions)."

The changes are well-scoped to the Perps feature domain. No changes to core Engine, navigation, browser, accounts, networks, or other unrelated features. The new getExchangeClient() method in HyperLiquidProvider is an admin/test escape hatch that doesn't affect production flows. The test ID addition to PerpsWithdrawView is a minor enhancement for test automation.

Performance Test Selection:
The changes modify how the Perps account balance is calculated and displayed (availableToTradeBalance vs availableBalance). This affects the PerpsMarketBalanceActions component (balance display), PerpsMarketDetailsView (Add Funds CTA), and the order form initialization. The @PerformancePreps tag covers perps market loading, position management, add funds flow, and balance display - all of which are directly impacted by this balance calculation change. The new getAvailableToTradeSpotBalance() utility function adds computation to the account state aggregation path which could affect rendering performance.

View GitHub Actions results

pull Bot pushed a commit to Pe44e/metamask-mobile that referenced this pull request Apr 22, 2026
…29203)

## **Description**

Adds agentic workflow json directly to main, separately from cherry pick
branch: MetaMask#29150

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: Adds MetaMask#29510 agentic workflows separately from cherry
pick branch

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

- [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)).
Not required for external contributors.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] 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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: only adds internal agentic JSON workflows and a docs link,
with no runtime app code changes. Main risk is operational (admin flow
invokes mainnet HyperLiquid transfers if executed).
> 
> **Overview**
> Adds two new Perps agentic flows: `hl-provision-fixture.json`
(admin/test automation to flip HyperLiquid abstraction mode and transfer
USDC between perps/spot via the exchange client) and
`hl-balance-validation.json` (automation that refreshes account state,
asserts `availableBalance` vs `availableToTradeBalance` invariants, and
screenshots/validates balance text on market list, withdraw, and order
entry screens).
> 
> Extends `evals.json` with a reusable `hl-fixture-state` snapshot
helper used by the new flows, and updates `perps-architecture.md` to
link to the new HyperLiquid account-modes/portfolio-margin doc.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b14ef5e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
12 value mismatches detected (expected — fixture represents an existing user).
View details

@abretonc7s abretonc7s added the DO-NOT-MERGE Pull requests that should not be merged label Apr 23, 2026
@geositta
Copy link
Copy Markdown
Contributor Author

Closing in favor of starting new branch to handle through spotState websocket subscription.

@geositta geositta closed this Apr 23, 2026
auto-merge was automatically disabled April 23, 2026 02:02

Pull request was closed

@github-actions github-actions Bot locked and limited conversation to collaborators Apr 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

DO-NOT-MERGE Pull requests that should not be merged risk:high AI analysis: high risk risk-medium Moderate testing recommended · Possible bug introduction risk size-L team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants