Skip to content

feat(social-controllers): expose perp metadata on Position and Trade types#9094

Merged
joaosantos15 merged 3 commits into
mainfrom
core-TSA-hyperliquid
Jun 16, 2026
Merged

feat(social-controllers): expose perp metadata on Position and Trade types#9094
joaosantos15 merged 3 commits into
mainfrom
core-TSA-hyperliquid

Conversation

@joaosantos15

@joaosantos15 joaosantos15 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Explanation

The mobile Perps leaderboard/position UX needs typed, validated access to the perp metadata that social-api now returns for Hyperliquid (and other perp) positions. Today @metamask/social-controllers drops those fields on the floor because they aren't declared on the response types or their superstruct schemas.

This PR extends the SocialService response types and validation schemas with the optional perp fields:

  • Trade (and TradeStruct): adds classification ('spot' | 'perp' | 'send' | 'receive' | null), perpPositionType ('long' | 'short' | null), and perpLeverage (number | null).
  • Position (and PositionStruct): adds perpPositionType, perpLeverage, and positionAmountWithLeverage (the leveraged/notional position size — Hyperliquid/perp only).

All fields are optional and nullable, so spot responses validate exactly as before — this is a purely additive, non-breaking change. This is the client-side half of the Hyperliquid perps work; the matching server-side passthrough lives in social-api.

Audit follow-up (TSA-726): corrected the positionAmountWithLeverage JSDoc — it is the leveraged/notional size as reported by Clicker (not necessarily positionAmount × perpLeverage; the ratio varies across multi-fill positions) and is notional exposure, not capital at risk (the margin/capital at risk is costBasis). This keeps consumers from mis-deriving leverage or risk from the field; the perp PnL fix that depends on this contract lives in consensys-vertical-apps/va-mmcx-social-api#86.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Additive optional/nullable fields and stricter validation only when new perp keys are present; spot payloads without those fields behave as before.

Overview
Adds optional, nullable perp/Hyperliquid metadata to social position and trade models so SocialService no longer strips fields the API already returns.

Trade / TradeStruct now include classification (spot | perp | send | receive), perpPositionType, and perpLeverage. Position / PositionStruct add perpPositionType, perpLeverage, and positionAmountWithLeverage (leveraged size / capital at risk). Matching superstruct rules on PositionStruct validate these on open/closed positions and fetchPositionById.

Tests cover passthrough of perp payloads, explicit null for spot, and rejection of invalid enum values (e.g. bad perpPositionType). Changelog documents the additive API surface for consumers (e.g. mobile Perps UI).

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

…ypes

Adds optional Hyperliquid/perp fields to the `SocialService` response
types and their superstruct validation schemas so consumers (mobile)
get typed, validated access to the perp metadata that social-api now
returns:

- `Trade`: `classification`, `perpPositionType`, `perpLeverage`
- `Position`: `perpPositionType`, `perpLeverage`, `positionAmountWithLeverage`

All fields are optional and nullable, so spot responses remain backward
compatible. Part of the Hyperliquid perps leaderboard/positions work
(TSA-629).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joaosantos15
joaosantos15 requested review from a team as code owners June 11, 2026 12:35
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joaosantos15

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-previews

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.2-preview-a3e0ef7eb
@metamask-previews/accounts-controller@39.0.1-preview-a3e0ef7eb
@metamask-previews/address-book-controller@7.1.2-preview-a3e0ef7eb
@metamask-previews/ai-controllers@0.7.0-preview-a3e0ef7eb
@metamask-previews/analytics-controller@1.1.1-preview-a3e0ef7eb
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-a3e0ef7eb
@metamask-previews/announcement-controller@8.1.0-preview-a3e0ef7eb
@metamask-previews/app-metadata-controller@2.0.1-preview-a3e0ef7eb
@metamask-previews/approval-controller@9.0.2-preview-a3e0ef7eb
@metamask-previews/assets-controller@9.0.1-preview-a3e0ef7eb
@metamask-previews/assets-controllers@109.0.0-preview-a3e0ef7eb
@metamask-previews/authenticated-user-storage@2.0.0-preview-a3e0ef7eb
@metamask-previews/base-controller@9.1.0-preview-a3e0ef7eb
@metamask-previews/base-data-service@0.1.3-preview-a3e0ef7eb
@metamask-previews/bridge-controller@75.1.1-preview-a3e0ef7eb
@metamask-previews/bridge-status-controller@72.1.0-preview-a3e0ef7eb
@metamask-previews/build-utils@3.0.4-preview-a3e0ef7eb
@metamask-previews/chain-agnostic-permission@1.6.1-preview-a3e0ef7eb
@metamask-previews/chomp-api-service@3.1.0-preview-a3e0ef7eb
@metamask-previews/claims-controller@0.5.3-preview-a3e0ef7eb
@metamask-previews/client-controller@1.0.1-preview-a3e0ef7eb
@metamask-previews/compliance-controller@2.1.0-preview-a3e0ef7eb
@metamask-previews/composable-controller@12.0.1-preview-a3e0ef7eb
@metamask-previews/config-registry-controller@0.4.1-preview-a3e0ef7eb
@metamask-previews/connectivity-controller@0.2.0-preview-a3e0ef7eb
@metamask-previews/controller-utils@12.2.0-preview-a3e0ef7eb
@metamask-previews/core-backend@6.3.3-preview-a3e0ef7eb
@metamask-previews/delegation-controller@3.0.2-preview-a3e0ef7eb
@metamask-previews/earn-controller@12.2.0-preview-a3e0ef7eb
@metamask-previews/eip-5792-middleware@3.0.4-preview-a3e0ef7eb
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-a3e0ef7eb
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-a3e0ef7eb
@metamask-previews/ens-controller@19.1.3-preview-a3e0ef7eb
@metamask-previews/eth-block-tracker@15.0.1-preview-a3e0ef7eb
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-a3e0ef7eb
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-a3e0ef7eb
@metamask-previews/foundryup@1.0.1-preview-a3e0ef7eb
@metamask-previews/gas-fee-controller@26.2.2-preview-a3e0ef7eb
@metamask-previews/gator-permissions-controller@4.2.0-preview-a3e0ef7eb
@metamask-previews/geolocation-controller@0.1.3-preview-a3e0ef7eb
@metamask-previews/json-rpc-engine@10.5.0-preview-a3e0ef7eb
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-a3e0ef7eb
@metamask-previews/keyring-controller@27.0.0-preview-a3e0ef7eb
@metamask-previews/logging-controller@8.0.2-preview-a3e0ef7eb
@metamask-previews/message-manager@14.1.2-preview-a3e0ef7eb
@metamask-previews/messenger@1.2.0-preview-a3e0ef7eb
@metamask-previews/messenger-cli@0.2.0-preview-a3e0ef7eb
@metamask-previews/money-account-balance-service@1.0.2-preview-a3e0ef7eb
@metamask-previews/money-account-controller@0.3.3-preview-a3e0ef7eb
@metamask-previews/money-account-upgrade-controller@2.0.5-preview-a3e0ef7eb
@metamask-previews/multichain-account-service@10.0.3-preview-a3e0ef7eb
@metamask-previews/multichain-api-middleware@3.1.3-preview-a3e0ef7eb
@metamask-previews/multichain-network-controller@3.1.3-preview-a3e0ef7eb
@metamask-previews/multichain-transactions-controller@7.1.1-preview-a3e0ef7eb
@metamask-previews/name-controller@9.1.2-preview-a3e0ef7eb
@metamask-previews/network-controller@32.0.0-preview-a3e0ef7eb
@metamask-previews/network-enablement-controller@5.3.0-preview-a3e0ef7eb
@metamask-previews/notification-services-controller@24.1.3-preview-a3e0ef7eb
@metamask-previews/passkey-controller@2.0.1-preview-a3e0ef7eb
@metamask-previews/permission-controller@13.1.1-preview-a3e0ef7eb
@metamask-previews/permission-log-controller@5.1.0-preview-a3e0ef7eb
@metamask-previews/perps-controller@8.1.0-preview-a3e0ef7eb
@metamask-previews/phishing-controller@17.2.0-preview-a3e0ef7eb
@metamask-previews/polling-controller@16.0.6-preview-a3e0ef7eb
@metamask-previews/preferences-controller@23.1.0-preview-a3e0ef7eb
@metamask-previews/profile-metrics-controller@3.2.0-preview-a3e0ef7eb
@metamask-previews/profile-sync-controller@28.1.1-preview-a3e0ef7eb
@metamask-previews/ramps-controller@14.1.1-preview-a3e0ef7eb
@metamask-previews/rate-limit-controller@7.0.1-preview-a3e0ef7eb
@metamask-previews/react-data-query@0.2.1-preview-a3e0ef7eb
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-a3e0ef7eb
@metamask-previews/sample-controllers@5.0.1-preview-a3e0ef7eb
@metamask-previews/seedless-onboarding-controller@10.0.2-preview-a3e0ef7eb
@metamask-previews/selected-network-controller@26.1.3-preview-a3e0ef7eb
@metamask-previews/shield-controller@5.1.2-preview-a3e0ef7eb
@metamask-previews/signature-controller@39.2.5-preview-a3e0ef7eb
@metamask-previews/snap-account-service@0.3.1-preview-a3e0ef7eb
@metamask-previews/social-controllers@2.2.1-preview-a3e0ef7eb
@metamask-previews/storage-service@1.0.2-preview-a3e0ef7eb
@metamask-previews/subscription-controller@6.2.0-preview-a3e0ef7eb
@metamask-previews/transaction-controller@68.0.0-preview-a3e0ef7eb
@metamask-previews/transaction-pay-controller@23.5.1-preview-a3e0ef7eb
@metamask-previews/user-operation-controller@41.2.4-preview-a3e0ef7eb
@metamask-previews/wallet@3.0.0-preview-a3e0ef7eb

zone-live
zone-live previously approved these changes Jun 12, 2026
Bigshmow
Bigshmow previously approved these changes Jun 15, 2026
…on type

positionAmountWithLeverage is the leveraged/notional size as reported by Clicker;
it is not necessarily positionAmount x perpLeverage (the ratio varies for positions
built across fills at different leverage) and it is notional exposure, not capital
at risk (the margin is costBasis). Correct the JSDoc so consumers use the field
directly and treat perpLeverage as the authoritative leverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joaosantos15
joaosantos15 dismissed stale reviews from Bigshmow and zone-live via 3934fc8 June 16, 2026 09:05
@joaosantos15
joaosantos15 enabled auto-merge June 16, 2026 09:27
@joaosantos15
joaosantos15 added this pull request to the merge queue Jun 16, 2026
Merged via the queue into main with commit a082eef Jun 16, 2026
375 checks passed
@joaosantos15
joaosantos15 deleted the core-TSA-hyperliquid branch June 16, 2026 09:41
pull Bot pushed a commit to Reality2byte/metamask-mobile that referenced this pull request Jun 17, 2026
…28) (MetaMask#31570)

## **Description**

Adds Hyperliquid (perps) support to the Social Leaderboard UI — the
second step of the perps work, consuming the perp fields now exposed by
`@metamask/social-controllers` and the social API.

**What changed**
- New shared primitives: a `PerpBadges` component (leverage pill +
`LONG`/`SHORT` pill) and `utils/perp.ts` helpers
(`isPerpPosition`/`isPerpTrade` + long/short direction resolution), so
perp classification lives in one place.
- `getPositionNetworkBadge` resolves the Hyperliquid network logo
(HyperEVM image source) for the token avatar badge, without adding
`hyperliquid` to `chainNameToId` (so the spot price/QuickBuy paths still
treat it as unsupported).
- **Trader profile** position rows show the HL network badge + leverage
+ long/short for perps; spot rows are unchanged.
- **Position detail** header shows the same badges; trade rows read
"opened/closed" with leverage + direction for perp fills.
- The position detail footer swaps the single **Buy** CTA for
**Long/Short** buttons on perps (intentional placeholders — not wired to
a trade flow yet). The chart shows its existing no-data placeholder
since perp pricing isn't available yet.
- Pins `@metamask/social-controllers` to the perps preview build so the
new `Position`/`Trade` perp fields are typed/available.

**Motivation:** unblock the mobile Perps leaderboard/position UX (perp
metadata was being dropped because the client types/UI didn't model it).

**Perp PnL display corrections (audit follow-up — TSA-726)**
- Position rows now render the **leveraged** size
(`positionAmountWithLeverage`) for perps, so the displayed size, the
leverage badge, and the (now leverage-correct) PnL coming from the API
cohere.
- `getPerpPositionDirection` no longer infers a side from the sign of
`positionAmount` — Clicker reports perp size as a positive magnitude and
conveys direction only via `perpPositionType`, so the sign is not a
reliable signal (HL spot tokens can carry a negative amount with no
side). It returns `null` when the side is absent, removing fabricated
`LONG`/`SHORT` badges. Pairs with the server-side PnL fix in
consensys-vertical-apps/va-mmcx-social-api#86.

## **Changelog**

CHANGELOG entry: Added Hyperliquid perpetuals to the Social Leaderboard
— trader profiles and position details now show leverage, long/short
direction, and the Hyperliquid network badge.

## **Related issues**

Fixes: TSA-628

Part of: TSA-726 (mobile display portion — perp PnL is computed
server-side in consensys-vertical-apps/va-mmcx-social-api#86)

Depends on:
- MetaMask/core#9094 (`@metamask/social-controllers` perp fields —
pinned here as a preview build until released)
- consensys-vertical-apps/va-mmcx-social-api#86 (perp DTO passthrough +
`chains=all` + gated perp notifications)

## **Manual testing steps**

```gherkin
Feature: Hyperliquid perps in the Social Leaderboard

  Scenario: viewing a trader's perp positions
    Given a trader has open Hyperliquid perp positions
    When I open their profile in the Social Leaderboard
    Then each perp position shows the Hyperliquid network badge, its leverage (e.g. 5x), and LONG/SHORT
    And spot positions render unchanged

  Scenario: opening a perp position detail
    Given I am on a trader profile with a perp position
    When I tap that position
    Then the header shows the token with its leverage and long/short badges
    And the trades list shows "opened"/"closed" rows with leverage + direction
    And the footer shows Long and Short buttons (placeholders) instead of Buy
    And the chart shows the no-data placeholder
```

> Local setup: point `SOCIAL_API_URL` at a social-api running the perps
branch (`va-mmcx-social-api#86`), which serves Hyperliquid positions via
`chains=all`.

## **Screenshots/Recordings**

### **Before**

Spot-only positions; no leverage/direction/HL badge.

### **After**

_To be captured on-device before marking Ready for review_ — matches the
approved designs (profile list with `5x LONG` / `8x SHORT` rows + HL
badge, and the position detail with leverage/direction badges and
Long/Short footer).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Bigshmow <devin.stewart@consensys.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants