Skip to content

[pull] main from MetaMask:main#628

Merged
pull[bot] merged 8 commits into
Reality2byte:mainfrom
MetaMask:main
Mar 25, 2026
Merged

[pull] main from MetaMask:main#628
pull[bot] merged 8 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Mar 25, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

sophieqgu and others added 8 commits March 25, 2026 07:27
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
Use updated image field from CampaignDto to render campaign tile
background.
## **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: null

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

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

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] 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]
> **Medium Risk**
> Medium risk because it changes the `CampaignDto` shape (removing
`statusLabel`, moving `details.image` to top-level `image`) and updates
UI rendering to depend on the new field, which could break if upstream
data isn’t migrated consistently.
> 
> **Overview**
> Rewards campaign data is updated to **remove `statusLabel`** and
introduce an optional top-level **`image`** (theme-aware URLs), with
`details.image` removed from the type/state shapes.
> 
> UI components (`CampaignTile`, `CampaignStatus`) now render their
background images from `campaign.image` instead of
`campaign.details.image`, and tests across rewards
views/controller/reducer are updated to match the new DTO shape.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5cf9e9f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
#27873)

…ind CSS

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

This PR migrates the ChoosePassword view and its FoxRiveLoaderAnimation
sub-component away from legacy StyleSheet.create()-based styling toward
the MetaMask design system and Tailwind CSS.

Jira Link: https://consensyssoftware.atlassian.net/browse/TO-636

## **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: migrate reveal-srp-ui to design system components and
Tailwind CSS

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



https://github.com/user-attachments/assets/719aeac1-bb97-424f-af35-7cba9cf84d50



https://github.com/user-attachments/assets/e4e16264-12ed-40be-9d0c-66947522b91e



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

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] 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]
> **Medium Risk**
> Primarily a UI/styling refactor, but it touches Secret Recovery Phrase
reveal/quiz screens where regressions could impact critical security UX
and automated flows (Detox selectors/accessibility).
> 
> **Overview**
> Migrates `RevealPrivateCredential` (SRP quiz, password entry, SRP
text/QR tabs, seed phrase concealer/display) from legacy
component-library buttons/text + `StyleSheet.create()` to
**design-system React Native components** with **Tailwind
(`twClassName`/`tw.style`)** and deletes
`RevealPrivateCredential/styles.ts` plus the `styles` prop wiring in
related components/types.
> 
> Reuses the shared `SeedPhraseConcealer` in `ManualBackupStep1`
(replacing an inline `ImageBackground` blur implementation) and updates
the snapshot accordingly.
> 
> Improves testability by adding an `accessibilityLabel` to the password
field and updating the Detox page object to locate it via label instead
of testID.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d5fa10c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…27861)

## **Description**

Adds two new reference documents for the Perps domain:
- **perps-caching-architecture.md** — Documents the three-tier caching
strategy (real-time WS, session, legacy preload), explains how data
flows, and identifies legacy preload as partially redundant now that
`PerpsAlwaysOnProvider` keeps WS connected from wallet mount.
- **perps-review-antipatterns.md** — Catalogues domain-specific
anti-patterns to watch for during code review (controller portability,
magic strings, placeholder values, etc.).

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

Documentation only — no runtime changes.

## **Screenshots/Recordings**

N/A

## **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
- [ ] I've included tests if applicable
- [ ] 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.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: adds documentation only and does not change runtime
behavior, APIs, or data handling.
> 
> **Overview**
> Adds a new `perps-caching-architecture.md` reference documenting the
current three-tier Perps caching model (WS real-time, session/provider
caches, and legacy REST preload), including cache lifetimes/clearing
behavior and a roadmap to simplify (disk-backed cold-start cache,
removing REST polling, and unifying DEX discovery caches to prevent
desync).
> 
> Adds `perps-review-antipatterns.md`, a code-review checklist of
Perps-specific pitfalls (controller portability for core sync, avoiding
magic constants/placeholders, enforcing provider abstraction, consistent
metrics/tracing, WS lifecycle rules, cache invalidation, and testID
requirements for agentic/E2E testability).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2511ffb. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

ERC1155 NFTs were not sendable from the NFT details screen because the
`isTradable` guard only allowed `ERC721` tokens, even though the
underlying send flow already supports ERC1155.

This PR:
1. **Enables the "Send" button for ERC1155 tokens** in `NftDetails` by
extending `isTradable` to accept both `ERC721` and `ERC1155` standards
when `isCurrentlyOwned` is true.
2. **Adds a loading skeleton on the Amount screen** while `useEVMNfts`
asynchronously processes the NFT (resolves IPFS image URLs, fetches
ERC1155 balance, computes `networkBadgeSource`). Without this, the
screen briefly shows `0 units available` and no image before the data
arrives.
3. **Replaces the deprecated `StyledButton` with `Button`** from
`@metamask/design-system-react-native` on the NFT details send button,
using its built-in `isLoading` and `isDisabled` props to give feedback
during the network-switch step.

The loading state is propagated through `useEVMNfts` → `useRouteParams`
→ `Amount`, keeping the data flow explicit and avoiding any inference
from data absence (which would cause the skeleton to hang if the NFT is
not found).


<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **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: Added support for sending ERC1155 NFTs from the NFT
details screen

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-2908

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



https://github.com/user-attachments/assets/bfd2d8d7-1c67-4a87-8d38-ddef60bb7f55


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

## **Pre-merge author checklist**

- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] 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.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Moderate risk because it changes send entry conditions and refactors
`useEVMNfts` to be async with a new loading state that affects multiple
send screens; bugs could block NFT selection/sending or cause incorrect
asset resolution.
> 
> **Overview**
> **Enables sending owned ERC1155 NFTs from `NftDetails`** by widening
the tradable guard to include `ERC1155` (in addition to `ERC721`) and
updating the Send CTA to use the design-system `Button`.
> 
> **Improves Send Amount UX while NFT data resolves** by adding an
`isLoading` state to `useEVMNfts`, plumbing it through `useRouteParams`,
and showing skeleton placeholders (including balance) on the `Amount`
screen.
> 
> **Tightens NFT resolution for send navigation** by matching NFTs by
`address` + `chainId` + `tokenId` (important for ERC1155), and updates
mocks/snapshots/tests accordingly (including an ERC721 mock and new
loading/error-path coverage).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
068ef91. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…on) (#27685)

## **Description**

Stop loss and take profit pills were not rendering on activity rows
because the HyperLiquid fills API does not return order type
information. Fills now get enriched with `detailedOrderType` by
cross-referencing historical orders at the provider level. Additionally,
the merge logic in both `usePerpsHomeData` and
`usePerpsTransactionHistory` now preserves enrichment when WebSocket
fills overwrite REST fills. Also fixes the "Liquidated" pill rendering
on a separate row by adding the missing `fillTag` style with
`flexDirection: 'row'` to PerpsTransactionsView.styles.ts.

## **Changelog**

CHANGELOG entry: Fixed stop loss and take profit pills not appearing on
recent activity rows in Perps Home and Activity screens

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Activity pill rendering for TP/SL orders
  Scenario: Stop loss pill on closed position
    Given a position was closed via stop loss
    When the user views recent activity on Perps Home
    Then the activity row shows a "Stop loss" pill

  Scenario: Take profit pill on closed position
    Given a position was closed via take profit
    When the user views the Activity page
    Then the activity row shows a "Take profit" pill

  Scenario: No regression on other pill types
    Given a position was liquidated
    When the user views the Activity page
    Then the "Liquidated" pill renders inline on the Closed Long row
```

## **Screenshots/Recordings**

### **Before**
Pills missing on activity rows for TP/SL closes. "Liquidated" pill on
separate row instead of inline with "Closed Long".


https://github.com/user-attachments/assets/ef394ef1-97dc-48a9-9780-292bcfd09cd7


### **After**
- Video: `automation/27685/after.mp4` — Perps Activity page with fix
applied
- CDP eval evidence: 4/4 TP/SL fills enriched (`Stop Market`, `Take
Profit Limit`)
- `fillTag` style added to ensure pills render inline (row layout)


https://github.com/user-attachments/assets/4aa46bef-6bf5-478c-8769-2f0f08509344


## **Validation Recipe**

<details>
<summary>Automated validation recipe (validate-recipe.sh)</summary>

```json
{
  "pr": "27685",
  "title": "Stop loss and take profit pills appear in recent activity",
  "jira": "TAT-2668",
  "acceptance_criteria": [
    "Stop loss and take profit pills appear inline on the activity row when a position is closed via stop loss or take profit",
    "The Liquidated pill renders on the same row as the Closed Long label",
    "No other activity pill types are broken or misaligned",
    "Behavior is consistent across Perps Home, Perp Market screen, and Activity page"
  ],
  "validate": {
    "static": ["yarn lint:tsc"],
    "runtime": {
      "pre_conditions": [
        "CDP connected",
        "Wallet unlocked on Wallet route",
        "Active account has TP/SL fill history (e.g. 0x316bde)"
      ],
      "steps": [
        {
          "id": "nav_activity",
          "description": "Navigate to Perps Activity with Trades tab",
          "action": "navigate",
          "target": "PerpsActivity",
          "params": { "redirectToPerpsTransactions": true }
        },
        {
          "id": "wait_load",
          "description": "Wait for transaction data to load",
          "action": "wait",
          "ms": 8000
        },
        {
          "id": "verify_enrichment",
          "description": "Verify REST fills are enriched with detailedOrderType from historical orders",
          "action": "eval_async",
          "expression": "Engine.context.PerpsController.getActiveProviderOrNull().getOrderFills({aggregateByTime:false}).then(function(fills){var tpsl=fills.filter(function(f){return f.detailedOrderType&&(f.detailedOrderType.indexOf('Stop')>=0||f.detailedOrderType.indexOf('Take Profit')>=0)});return JSON.stringify({total:fills.length,tpslCount:tpsl.length})})",
          "assert": { "operator": "not_null" }
        },
        {
          "id": "scroll_flashlist",
          "description": "Scroll the FlashList to verify testID targeting works",
          "action": "scroll",
          "test_id": "perps-transactions-flash-list",
          "offset": 400
        },
        {
          "id": "scroll_back",
          "description": "Scroll back to top to show first rows with pills",
          "action": "scroll",
          "test_id": "perps-transactions-flash-list",
          "offset": 0
        },
        {
          "id": "check_no_errors",
          "description": "No errors in Metro logs during Activity page load",
          "action": "log_watch",
          "window_seconds": 5,
          "must_not_appear": ["TypeError", "undefined is not an object"]
        },
        {
          "id": "screenshot_evidence",
          "description": "Screenshot showing TP/SL pills on activity rows",
          "action": "screenshot",
          "filename": "activity-tpsl-pills.png"
        }
      ]
    }
  }
}

```

</details>

## **Pre-merge author checklist**

- [x] I've followed MetaMask Contributor Docs and Coding Standards
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using JSDoc format if applicable
- [x] I've applied the right labels on the PR

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes how REST and WebSocket fills are merged
and enriched across provider, subscription, and UI activity hooks, which
can affect transaction history correctness and ordering.
> 
> **Overview**
> Restores *Take Profit/Stop Loss* (and related) pills in Perps activity
by enriching HyperLiquid `getOrderFills` responses with
`detailedOrderType` via a parallel `historicalOrders` lookup, and
enriching WebSocket fills using cached order data.
> 
> Updates merge/dedup logic in `usePerpsHomeData` and
`usePerpsTransactionHistory` to **preserve REST-derived enrichment**
(including liquidation details / non-`Standard` `fillType`) when live WS
data overwrites duplicates, with new unit tests covering these cases.
> 
> Adds E2E selectors (`FlashList` + fill tag testIDs) and a small layout
fix (`fillTag` row style) so pills render inline and are targetable in
automation.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
71223e1. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ent margin rejections (#27417)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

When users place a long/short Perps order with the amount slider at 100%
(or tap "Max"), the app was sending the full theoretical maximum
(available balance × leverage). The HyperLiquid API sometimes rejects
these with "Order 0: Insufficient margin to place order" due to fees,
rounding, and exchange-side margin checks.

**Solution:** Introduce a 0.5% margin buffer on the maximum order amount
so that "100%" uses 99.5% of the theoretical max. This is applied in a
single place (`getMaxAllowedAmount`), and the order form uses this
buffered value for the slider max, Max button, and 100% quick button so
all paths stay consistent. The buffer is configurable via
`MAX_ORDER_MARGIN_BUFFER` in perps config for future tuning or smarter
logic (e.g. fee-based).

**Changes:**
- **perpsConfig**: Added `MAX_ORDER_MARGIN_BUFFER = 0.005` (0.5%).
- **getMaxAllowedAmount**: After existing rounding logic, multiply max
by `(1 - MAX_ORDER_MARGIN_BUFFER)` and return `floor(bufferedMax)`.
- **usePerpsOrderForm**: `handleMaxAmount` and
`handlePercentageAmount(1)` now set amount to `maxPossibleAmount` (the
buffered max) instead of computing `balance × leverage` directly.
- **Tests**: Updated expectations for low-balance scenarios (e.g. $2 @
3x → max 5 instead of 6); added test that max is below theoretical after
buffer.

## **Changelog**

CHANGELOG entry: Fixed Perps orders at 100% margin sometimes failing
with "Insufficient margin" by applying a small buffer to the maximum
order amount.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-2502

## **Manual testing steps**

```gherkin
Feature: Perps order placement at maximum margin

  Scenario: user places order at 100% (slider or Max) without insufficient margin error
    Given user is on Perps order view with available balance and an asset selected
    When user sets amount to 100% via the slider or taps the Max / 100% button
    Then the amount field shows the buffered max (slightly below theoretical max)
    And placing the order does not result in "Insufficient margin" rejection from the exchange
```

## **Screenshots/Recordings**

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

### **Before**

N/A (behavioral fix; no UI change)

### **After**

N/A

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

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adjusts core order-sizing calculations for max/percentage selections,
which can affect how much users trade and may surface edge cases around
rounding and balance updates. Changes are localized and covered by
updated/additional tests.
> 
> **Overview**
> Reduces Perps *maximum order amount* by applying a configurable **0.5%
margin buffer** so “Max”/100% selections are less likely to be rejected
as *Insufficient margin*.
> 
> This adds `MAX_ORDER_MARGIN_BUFFER` and applies it in
`getMaxAllowedAmount`, then updates `usePerpsOrderForm` handlers to
clamp percentage-based amounts and set Max to `maxPossibleAmount` (the
buffered max). Tests are updated to reflect new buffered expectations
and add coverage for near-100% clamping and buffered-max behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
03168c3. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
Seedless/OAuth rehydration was prompting biometrics with a wrong
password entry too

**Solution**
Now the flow is password-first: unlockWallet runs with password only.
After a successful unlock, post-unlock steps (including optional
biometric upgrade). Failed unlocks no longer trigger biometric prompts.

Jira: https://consensyssoftware.atlassian.net/browse/TO-600

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **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: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TO-600

## **Manual testing steps**

```gherkin
Feature: Seedless OAuth rehydration password before biometrics

 Scenario: Wrong password does not trigger biometrics before unlock
    Given the user is on the OAuth rehydration screen with a seedless wallet
    And device biometrics are available

    When the user enters an incorrect password and submits
    Then the app shows an invalid password error
    And the system biometric prompt is not shown before unlock fails

  Scenario: Correct password offers biometrics after successful unlock (rehydration)
    Given the user is on the OAuth rehydration screen
    And device biometrics are available

    When the user enters the correct password and submits
    Then unlock completes successfully
    And the app may prompt for device biometrics / keychain upgrade only after unlock succeeds

```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**


https://github.com/user-attachments/assets/e5ade971-0f7e-4316-b272-9f2aa7c58fb8

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

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the seedless/OAuth rehydration login flow and post-unlock auth
preference updates, which can affect authentication UX and keychain
storage behavior. Scope is limited to `OAuthRehydration` and
adds/adjusts tests to cover ordering and failure cases.
> 
> **Overview**
> Updates `OAuthRehydration` so biometric/device-auth prompts no longer
occur *before* password verification: `unlockWallet` is called with
`currentAuthType: PASSWORD`, and an optional post-unlock step
(`upgradeKeychainAuthAfterSuccessfulUnlock`) requests device auth and
persists the result via `updateAuthPreference`.
> 
> Extends `OAuthRehydration` tests to assert call ordering (unlock
precedes biometrics) and to ensure biometrics/auth-preference updates
are not triggered when password unlock fails, including the
outdated-password flow.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f0c3963. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…on, env var centralization (#27899)

## **Description**

Resolves several core-parity and architecture concerns for the perps
controller:

### 1. Backport core-only changes (existing)
- **`stopEligibilityMonitoring()`** — Disables geo-blocking eligibility
checks when `useExternalServices` is toggled off.
- **Dynamic MYX import** — `await import()` → `.then()/.catch()` to
avoid bundling heavy MYX dependencies in extension.

### 2. Nested `providerCredentials` on `PerpsControllerConfig`
- Restructures flat MYX config fields (`myxAppIdTestnet`,
`myxProviderEnabled`, etc.) into `providerCredentials.myx.*`.
- Adds `providerCredentials.hyperliquid.*` for builder fee wallet
addresses.
- New types: `PerpsProviderCredentials`, `HyperLiquidCredentials`,
`MYXCredentials`.

### 3. Builder fee address injection
- `HyperLiquidProvider` accepts optional
`builderAddressTestnet`/`builderAddressMainnet` via constructor.
- Falls back to hardcoded `BUILDER_FEE_CONFIG` defaults when env vars
are empty.
- New env vars in `.js.env.example`:
`MM_PERPS_HL_BUILDER_ADDRESS_TESTNET`,
`MM_PERPS_HL_BUILDER_ADDRESS_MAINNET`.

### 4. Env var centralization in mobile adapter
- New `createMobileClientConfig()` in `mobileInfrastructure.ts` — all
`process.env.*` reads in one place.
- Engine init (`perps-controller/index.ts`) reduced from ~73 to ~37
lines — pure controller wiring, no env var reads.

### 5. MYX dynamic import race condition fix
- `await import()` inside non-async `#createProviders(): void` →
`.then()/.catch()` chain.
- Removes `@ts-expect-error` suppression. Fixes provider setup race
condition flagged by bugbot.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A — architecture cleanup + core parity

## **Manual testing steps**

```gherkin
Feature: Provider credentials and builder fee injection

  Scenario: HyperLiquid uses env-var builder address when set
    Given MM_PERPS_HL_BUILDER_ADDRESS_TESTNET is set in .js.env
    When a trade is placed on testnet
    Then the builder fee uses the env-var address (not hardcoded default)

  Scenario: HyperLiquid falls back to default when env var is empty
    Given MM_PERPS_HL_BUILDER_ADDRESS_TESTNET is empty
    When a trade is placed on testnet
    Then the builder fee uses BUILDER_FEE_CONFIG.TestnetBuilder

  Scenario: MYX provider registers via dynamic import
    Given MYX provider is enabled
    When PerpsController initializes
    Then MYX registers asynchronously via .then()/.catch()
    And initialization completes without waiting for MYX

  Scenario: Engine init uses adapter for config
    Given the app starts
    When PerpsController is initialized
    Then clientConfig comes from createMobileClientConfig()
    And no process.env reads exist in the Engine init file
```

## **Screenshots/Recordings**

N/A — no UI changes

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches Perps provider initialization/selection and geo-eligibility
monitoring, which can impact trading availability and protocol routing
if misconfigured. Changes are largely additive but include
async/dynamic-import ordering and new config wiring that should be
validated across networks/providers.
> 
> **Overview**
> **Refactors Perps controller configuration to use a nested
`providerCredentials` structure** and centralizes all Perps
`process.env` reads into `createMobileClientConfig()`, simplifying
`perpsControllerInit` to pure wiring.
> 
> **Adds HyperLiquid builder-fee address injection** via new env vars
and passes these through `PerpsController` into `HyperLiquidProvider`,
falling back to hardcoded defaults when env values are empty.
> 
> **Hardens MYX provider registration and eligibility controls** by
switching MYX to a dynamic `import()` flow with explicit error
handling/awaiting during initialization, adding
`stopEligibilityMonitoring()` (and messenger action typing) to defer
geolocation checks, and extending tests to cover these behaviors.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
30f6e0a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators Mar 25, 2026
@pull pull Bot added the ⤵️ pull label Mar 25, 2026
@pull pull Bot merged commit 62f6136 into Reality2byte:main Mar 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants