Skip to content

[pull] main from MetaMask:main#824

Merged
pull[bot] merged 12 commits into
Reality2byte:mainfrom
MetaMask:main
Jun 10, 2026
Merged

[pull] main from MetaMask:main#824
pull[bot] merged 12 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull

@pull pull Bot commented Jun 10, 2026

Copy link
Copy Markdown

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 : )

Matt561 and others added 12 commits June 10, 2026 18:48
… fees are subsidized (#31485)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**
Adds the "Paid by MetaMask" tag for Money account deposits when fees are
subsidized.
<!-- mms-check: type=text required=true -->

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

<!-- mms-check: type=changelog required=true -->

<!--
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 paid by metamask tag for Money account deposits
when fees are subsidized.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: [MUSD-954: Add "Paid by MetaMask" for subsidized Money account
deposits](https://consensyssoftware.atlassian.net/browse/MUSD-954)

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Paid by MetaMask tag for subsidized Money account deposits

  Scenario: Paid By MetaMask tag displayed on Money account deposit screen
    Given user has money account
    AND user has "no fee" token

    When user selects a token in the "Earn on your crypto" section that has a "no fee" token
    Then on Money account deposit screen the "Paid by MetaMask" tag is displayed next to the transaction fee
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

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

### **Before**

<!-- [screenshots/recordings] -->
N/A - We showed `Transaction Fee: $0`
### **After**

<!-- [screenshots/recordings] -->
<img width="505" height="1010" alt="image"
src="https://github.com/user-attachments/assets/90143fc4-3766-401b-8ee0-45e61d9c3fc9"
/>

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
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)

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**
> Small confirmation UI and hook logic change with tests; no auth or
payment execution paths modified.
> 
> **Overview**
> Extends **"Paid by MetaMask"** on the confirmation fee row to **Money
account deposits** when all pay fees are zero and quotes exist—the same
subsidized-fee behavior already used for mUSD conversion.
> 
> `useIsPaidByMetaMask` now treats `TransactionType.moneyAccountDeposit`
as supported and **returns false** when a fiat payment method is
selected (`selectedPaymentMethodId` from
`useTransactionPayFiatPayment`), so Apple Pay and similar flows do not
show the subsidized label. Tests cover the new deposit type in
`bridge-fee-row` and the fiat-payment guard in the hook.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9e02bc6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…31482)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->
When working on Main Navigator, I realized CollectiblesDetails route is
not in used anymore. This PR is to clean up this route and the
component:
- Remove the legacy `CollectiblesDetails` stack screen from
`MainNavigator`, which was registered but never navigated to
- Delete the orphaned `CollectibleModal` component and its tests,
styles, and types
- Remove the unused `InitSendLocation.CollectibleModal` send analytics
constant and the `CollectibleModal` CODEOWNERS entry

NFT detail flows already use the `NftDetails` screen (e.g. from
`NftGridItem`). The `/nft` deeplink also routes to `NFTS_FULL_VIEW`, not
this modal. No in-app navigation, deeplinks, or Braze campaigns
referenced `CollectiblesDetails`.

<img width="1206" height="2622"
alt="simulator_screenshot_123411D7-6C42-4AEE-A9F2-973B5197916D"
src="https://github.com/user-attachments/assets/477730f5-a752-4682-9716-776ee46e9155"
/>


## **Changelog**

<!-- mms-check: type=changelog required=true -->

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

<!-- mms-check: type=issue-link required=true -->

Fixes: clean up CollectiblesDetails route

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: NFT details navigation after CollectiblesDetails removal
  Background:
    Given I am logged into MetaMask Mobile
    And I have at least one NFT in my wallet
  Scenario: User can still open NFT details from the wallet
    When I open the wallet NFT list
    And I tap an NFT
    Then I should see the NFT details screen
  Scenario: NFT deeplink still opens the NFT list
    When I open the app via the "/nft" deeplink
    Then I should see the wallet NFT full view
  Scenario: Legacy CollectiblesDetails modal is no longer reachable
    When I use the app normally
    Then I should not see the old CollectibleModal bottom sheet

```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

N/A 

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
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)

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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`.
-->

- [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]
> **Low Risk**
> Deletion-only cleanup of unreachable navigation and UI; active NFT
flows use NftDetails and other existing routes.
> 
> **Overview**
> Removes dead NFT UI: the **`CollectiblesDetails`** stack screen is
unregistered from **`MainNavigator`**, and the entire
**`CollectibleModal`** folder (component, styles, types, tests) is
deleted.
> 
> Also drops **`InitSendLocation.CollectibleModal`** from send analytics
constants, the **`CollectibleModal`** CODEOWNERS line, and the navigator
test that asserted the removed screen. NFT detail flows continue to use
**`NftDetails`** and related routes already registered in the main
stack.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8610c73. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
… (TSA-657) (#31457)

## **Description**

<!-- mms-check: type=text required=true -->

The Quick Buy "Receive" view (sell flow) was missing the Solana, Tron,
and BTC network chips, so users couldn't select those chains to receive
on — breaking parity with the other supported networks.

Root cause: the receive-token candidates in `useReceiveTokens` were
built EVM-only — the stablecoin curation filtered
`DefaultSwapDestTokens` with `chainId.startsWith('0x')`, silently
dropping non-EVM (CAIP) chain ids. Since chips are derived from the
candidates' chain ids, no non-EVM candidates meant no chips. All
surrounding infrastructure (chain-filter display, enabled-network
gating, quote flow) already supported non-EVM chains.

Changes:

- **Solana:** SOL native and Solana USDC are now receive candidates.
- **Tron / Bitcoin:** native TRX and BTC are now receive candidates via
the bridge native-asset registry. Non-native tokens on these chains
(e.g. TRC-20 USDT) are deliberately excluded: `useAssetMetadata` can
only resolve Solana base58 / EVM hex addresses, so they cannot resolve
as Quick Buy destinations (see TSA-659).
- Each non-EVM chip is gated on the selected account actually having an
address on that chain (`selectSelectedInternalAccountByScope` per
scope), matching app-wide gating.
- `enrichSolanaTokenBalance` generalized to `enrichNonEvmTokenBalance`
so Tron/BTC holdings get correct balance/fiat enrichment (previously
Solana-only).
- The chain filter defaults to the position's CAIP chain for non-EVM
positions (previously fell back to "All").

Jira: [TSA-657](https://consensyssoftware.atlassian.net/browse/TSA-657)

## **Changelog**

<!-- mms-check: type=changelog required=true -->

CHANGELOG entry: Added Solana, Tron and Bitcoin network options to the
Quick Buy receive view

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: [TSA-657](https://consensyssoftware.atlassian.net/browse/TSA-657)

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Quick Buy receive view network chips

  Scenario: user with a multichain account opens the receive picker
    Given the selected account has Solana, Tron and Bitcoin addresses
    And the user is selling a token via Quick Buy

    When the user opens the "Receive" token picker
    Then Solana, Tron and Bitcoin chips appear alongside the EVM network chips
    And selecting Tron offers native TRX as the receive option
    And selecting Bitcoin offers native BTC as the receive option
    And selecting Solana offers SOL and Solana USDC

  Scenario: account without non-EVM addresses
    Given the selected account has no Tron or Bitcoin address

    When the user opens the "Receive" token picker
    Then the Tron and Bitcoin chips are not shown
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<img width="393" height="417" alt="image"
src="https://github.com/user-attachments/assets/414fe115-f979-4b8c-9ef2-6d7dfca49b1f"
/>

### **After**

<img width="437" height="423" alt="image"
src="https://github.com/user-attachments/assets/5cbf55a1-74ea-409d-aa22-6e6058a49b11"
/>

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**

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

<!-- Generated with the help of the pr-description AI skill -->


[TSA-657]:
https://consensyssoftware.atlassian.net/browse/TSA-657?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches sell-flow receive token selection and balance/fiat enrichment
across multichain accounts; wrong gating or chain-id mismatches could
hide options or misprice rows, but changes are scoped to Quick Buy with
broad test coverage.
> 
> **Overview**
> Fixes the Quick Buy **Receive** picker (sell flow) so **Solana, Tron,
and Bitcoin** network chips and tokens appear when the user’s account
and enabled networks allow it.
> 
> **Receive candidates (`useReceiveTokens`):** Stablecoin curation no
longer drops non-EVM chains—**Solana USDC** is included alongside EVM
stables. **Native TRX and BTC** are added via
`NATIVE_ONLY_NON_EVM_CHAINS` (TRC-20 stables on Tron stay excluded
because destinations can’t be resolved). Non-EVM options are gated on
**`selectSelectedInternalAccountByScope`** per chain and existing
network-enabled checks; multichain balances/rates are wired into
enrichment.
> 
> **Balances (`enrichTokenBalance`):** Solana-specific enrichment is
generalized to **`enrichNonEvmTokenBalance`** with Tron/Bitcoin accounts
so held TRX/BTC/SOL show correct fiat in the list.
> 
> **UI filter (`QuickBuyReceiveScreen`):** The default chain filter uses
the position’s **CAIP chain id** for non-EVM positions (matching
candidate `chainId` format) instead of skipping non-EVM and falling back
to “All”.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e4c8ac7. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## **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?
-->

The Card feature screens (`Routes.CARD.ROOT` -> `CardRoutes`) were
registered in the main navigator using the `ModalPresentationIOS`
transition preset. This caused the Card flow to be presented as an iOS
card-style modal stacked on top of the current screen, which did not
match the intended presentation for these screens and produced
inconsistent navigation behavior.

This PR switches the Card route presentation to
`ModalSlideFromBottomIOS`, so the Card screens slide up from the bottom
consistently with the rest of the modal flows in the app. The
corresponding test mock is updated to drop the now-unused
`ModalPresentationIOS` preset.

## **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: Fixed the presentation of the Card screens so they
slide up from the bottom.

## **Related issues**

Fixes: null

## **Manual testing steps**

```gherkin
Feature: Card screen presentation

  Scenario: user opens the Card flow
    Given the user is on a screen that can navigate to the Card feature
    When the user navigates to the Card flow
    Then the Card screens are presented sliding up from the bottom
    And the transition is consistent with other modal flows in the app
```

## **Screenshots/Recordings**

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

### **Before**

N/A

### **After**

N/A

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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.

<!-- Generated with the help of the pr-description AI skill -->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Single navigation transition preset change with no auth, data, or
routing logic changes.
> 
> **Overview**
> The Card stack (`Routes.CARD.ROOT` / `CardRoutes`) in `MainNavigator`
now uses **`TransitionPresets.ModalSlideFromBottomIOS`** instead of
**`ModalPresentationIOS`**, so the flow **slides up from the bottom**
rather than using the iOS card-style stacked modal.
> 
> The `@react-navigation/stack` test mock in `MainNavigator.test.tsx` is
updated to expose only **`ModalSlideFromBottomIOS`**, removing the
unused **`ModalPresentationIOS`** preset.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e5882e5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…te (TSA-632) (#31460)

## **Description**

<!-- mms-check: type=text required=true -->

The available balances shown in the Quick Buy sheet — the "Pay with"
balance (Buy) and the "Receive" balance (Sell) — only updated when a
Quick Buy trade reached a terminal state, and only really on the
pay-with side. Any balance change from another cause was missed: an
external incoming transfer, a swap in another flow, etc. Example: buy
$0.10 of USDC paying with ETH, wait for the "complete" toast — the
displayed ETH balance stayed at its pre-trade value.

Two stacked causes:

1. **Frozen token snapshot** — the selected pay-with / receive token is
a `useState` snapshot of one entry from the Redux-driven option lists;
the auto-select effect only runs while nothing is selected, so the token
object (including its cached `balance` / `balanceFiat`) froze at
selection time and never re-synced when the balance controllers updated
Redux.
2. **One-shot RPC fetch** — `useLatestBalance` fetches the EVM balance
once per token/account/`refreshKey`, and its fetched value shadows the
cached fallback. Quick Buy keyed it off a Quick-Buy-specific signal, so
it only re-fetched when a Quick Buy trade settled.

Fix — drive **both** displayed balances off live controller state so
they re-render on **any** change, regardless of origin:

- Add `resolveLiveTokenBalance`, which re-reads a selected token's
balance fields from the reactive option list it was picked from
(`usePayWithTokens` / `useReceiveTokens`). Those lists subscribe (via
`useSelector`) to `TokenBalancesController` / `AccountTrackerController`
(EVM) and the multichain balances state (non-EVM), so both the source
and dest balances track live controller state. Only balance fields are
read — never the `sourceToken` reference passed to quote fetching — so
market-data ticks can't churn quote requests.
- Re-key `useLatestBalance` off the live balance itself, so any change
to the underlying balance — for any reason — triggers a fresh on-chain
read and re-render, independent of Quick Buy's own state.
- Expose `destBalanceFiat` from the controller and drive the Sell-mode
footer pill from it (previously the frozen `selectedDestStable`
snapshot).
- Remove the terminal-toast `incrementBridgeBalanceRefreshKey` dispatch:
the live subscription already covers the settle case, so the trigger is
now "balance state changed," not "Quick Buy finished."

Jira: [TSA-632](https://consensyssoftware.atlassian.net/browse/TSA-632)

## **Changelog**

<!-- mms-check: type=changelog required=true -->

CHANGELOG entry: Fixed the Quick Buy "Pay with" and "Receive" available
balances not refreshing when the underlying balance changes (a swap
settling, an external transfer, a send in another flow, etc.)

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: [TSA-632](https://consensyssoftware.atlassian.net/browse/TSA-632)

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Quick Buy available balance freshness

  Scenario: pay-with balance updates when a Quick Buy swap settles
    Given the user notes their ETH on Base balance in the Quick Buy sheet
    And the user buys $0.10 of USDC on Base paying with ETH on Base
    When the user re-opens the Quick Buy sheet while the swap settles
    And the "complete" toast appears
    Then the displayed ETH on Base available balance updates to the post-trade value

  Scenario: pay-with balance updates on an external transfer
    Given the Quick Buy sheet is open in Buy mode with a pay-with token selected
    When the selected token's balance changes from outside Quick Buy (e.g. an incoming transfer)
    Then the displayed available balance updates without any Quick Buy action

  Scenario: receive balance updates on balance change (Sell mode)
    Given the Quick Buy sheet is open in Sell mode with a receive token selected
    When that token's balance changes (a sell settling, or an external transfer)
    Then the displayed receive available balance updates to the new value
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

**🔊 Sound ON**


https://github.com/user-attachments/assets/ddc84e7c-a6a2-4ec0-beee-b4e0a0e04d48

### **After**

**🔊 Sound ON**


https://github.com/user-attachments/assets/fc8e7fe3-e869-44f8-b6b1-369a946a4209


https://github.com/user-attachments/assets/2751d57f-7a23-42b9-bc06-a2e823c6fac1

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**

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

<!-- Generated with the help of the pr-description AI skill -->


[TSA-632]:
https://consensyssoftware.atlassian.net/browse/TSA-632?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches balance display, slider max spend, and on-chain balance
refresh in the trade flow; quote inputs intentionally keep frozen rates
to avoid extra quote churn.
> 
> **Overview**
> Fixes **TSA-632**: Quick Buy “Pay with” and “Receive” balances no
longer stick to the token snapshot taken at picker selection.
> 
> Adds **`resolveLiveTokenBalance`** to match the selected pay-with /
receive token against the reactive **`usePayWithTokens`** /
**`useReceiveTokens`** lists (by `address:chainId`) and read only
balance/rate fields so quote token references stay stable.
**`useQuickBuyController`** feeds live source balance into
**`useLatestBalance`** (with **`refreshKey`** tied to that balance),
uses **`liveSourceCurrencyExchangeRate`** for fiat display, slider cap,
and price migration, and exposes **`destBalanceFiat`** for sell mode.
**`QuickBuyActionFooter`** shows **`destBalanceFiat`** in sell mode
instead of the frozen **`selectedDestStable.balanceFiat`**.
> 
> Tests cover **`resolveLiveTokenBalance`** and controller behavior for
external transfers, rate-only updates, swap settlement, and list
fallback.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d6ef296. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Xavier Brochard <xavier-brochard@users.noreply.github.com>
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->

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

Migrated `HeaderBase` to `design-system-react-native` usage.

## **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/DSYS-282

## **Manual testing steps**

```gherkin
Feature: header base update

  Scenario: user visits screen with modified files
    Given user landed on the screen

    When user interacts with modified component
    Then they behave as before. No visual regressions or functional regressions
```

## **Screenshots/Recordings**

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

### **Before**

<img width="1206" height="2622" alt="image"
src="https://github.com/user-attachments/assets/fc63c63a-1053-4338-a8c2-ec8eca1b561b"
/>

### **After**

<img width="1206" height="2622" alt="image"
src="https://github.com/user-attachments/assets/c60cc494-0259-4dbc-be13-3e34a9449794"
/>

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**
> Import-only migration with unchanged HeaderBase usage on navigation
and modal headers; main risk is subtle visual or API differences between
local and package HeaderBase.
> 
> **Overview**
> This PR continues the **HeaderBase** design-system pilot by sourcing
it from `@metamask/design-system-react-native` instead of the local
`component-library` `HeaderBase` module.
> 
> **Navbar** drops the separate `HeaderBase` / `HeaderBaseVariant`
import and pulls `HeaderBase` from the same MMDS bundle as other
header-related primitives already used there. **Update needed** and **QR
tab switcher** only change their import paths; JSX still uses the same
props (`includesTopInset`, `twClassName`, `startAccessory` /
`endAccessory`, children).
> 
> No new screens or header behavior are introduced—this is an import
consolidation aimed at parity with the canonical design-system
component.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d5f4ffe. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

Integrates the required `feature_id` parameter from
[MetaMask/core#8964](MetaMask/core#8964) across
Unified Swap/Bridge, Batch Sell, and Quick Buy quote flows.

**Why:** `bridge-controller` now requires `FeatureId` on `fetchQuotes`
and on client-supplied analytics context for unified swap/bridge events.
Without it, quote fetches and some metrics payloads are incomplete or
fail type-checking against the new API.

**What changed:**

- **Unified Swap/Bridge:** Pass `feature_id: UNIFIED_SWAP_BRIDGE` in
`useUnifiedSwapBridgeContext` (quote polling context) and on client
`trackUnifiedSwapBridgeEvent` call sites
(`useTrackAllQuotesSortedEvent`, `GaslessQuickPickOptions`,
`BridgeTokenSelector` asset tooltip).
- **Batch Sell:** Pass `feature_id: BATCH_SELL` in
`useBatchSellQuoteRequest` quote context.
- **Quick Buy:** Add `getQuickBuyFeatureId` to map `QuickBuySheetSource`
→ `FeatureId` (`QUICK_BUY_TOKEN_DETAILS` vs `QUICK_BUY_FOLLOW_TRADING`);
thread analytics `source` through `useQuickBuyController` and pass the
resolved id to `BridgeController.fetchQuotes(params, featureId,
signal)`.
- **Tests:** Update/add unit tests for the above contexts and Quick Buy
quote fetching.

**Follow-up after core#8964 merges:** Remove `previewBuilds` /
resolutions and bump to released package versions.

## **Changelog**

<!-- mms-check: type=changelog required=true -->

CHANGELOG entry: null

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes:
[SWAPS-4561](https://consensyssoftware.atlassian.net/browse/SWAPS-4561)

Refs: [MetaMask/core#8964](MetaMask/core#8964)

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: feature_id on swap/bridge quote flows

  Scenario: Unified Swap/Bridge still fetches quotes
    Given the app is built with preview bridge-controller packages (yarn install)
    And I open Unified Swap/Bridge from a token or the wallet swap entry point
    When the swap form loads and requests quotes
    Then quotes load without errors and the swap flow remains usable

  Scenario: Batch Sell still fetches quotes
    Given Batch Sell is available in the build
    When I open Batch Sell and trigger a quote request
    Then quotes load without errors

  Scenario: Quick Buy still fetches quotes from follow-trading
    Given Social Leaderboard is enabled
    When I open a trader position and tap Buy to open Quick Buy
    Then the Quick Buy sheet loads quotes without error

  Scenario: Quick Buy still fetches quotes from token details
    Given socialAiAssetDetailsQuickBuy is enabled
    When I open Token Details and tap the Quick Buy (lightning) button
    Then the Quick Buy sheet loads quotes without error
```

**Automated verification run locally:**

```bash
yarn lint:tsc
yarn jest app/components/UI/Bridge/hooks/useUnifiedSwapBridgeContext/useUnifiedSwapBridgeContext.test.ts
yarn jest app/components/UI/Bridge/hooks/useBatchSellQuoteRequest/useBatchSellQuoteRequest.test.ts
yarn jest app/components/UI/Bridge/hooks/useTrackAllQuotesSortedEvent/index.test.ts
yarn jest app/components/UI/Bridge/hooks/useBridgeQuoteEvents/useBridgeQuoteEvents.test.tsx
yarn jest app/components/Views/SocialLeaderboard/TraderPositionView/components/QuickBuy/useQuickBuyQuotes.test.ts
yarn jest app/components/Views/SocialLeaderboard/TraderPositionView/components/QuickBuy/utils/getQuickBuyFeatureId.test.ts
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

N/A — analytics and controller plumbing only; no user-visible UI
changes.

### **Before**

N/A

### **After**


<img width="685" height="93" alt="Screenshot 2026-06-08 at 5 29 57 PM"
src="https://github.com/user-attachments/assets/17109fe7-2203-4379-8b07-042da3401d5d"
/>
<img width="737" height="155" alt="Screenshot 2026-06-08 at 5 30 15 PM"
src="https://github.com/user-attachments/assets/2ab8f297-2eb2-4eb7-adc8-a7e9c0eb37fe"
/>
<img width="685" height="181" alt="Screenshot 2026-06-08 at 5 30 26 PM"
src="https://github.com/user-attachments/assets/fb0bbd0a-a2c9-458c-bdac-3c0849e3e251"
/>
<img width="520" height="195" alt="Screenshot 2026-06-08 at 5 30 54 PM"
src="https://github.com/user-attachments/assets/211396df-29f6-47db-9663-4931b1c3605c"
/>
<img width="587" height="163" alt="Screenshot 2026-06-08 at 5 31 14 PM"
src="https://github.com/user-attachments/assets/84a4cda8-6dae-41d3-aed0-824dd62633f6"
/>
<img width="683" height="89" alt="Screenshot 2026-06-08 at 5 31 29 PM"
src="https://github.com/user-attachments/assets/9c9b1c9b-af89-4078-b43d-3a7322b89781"
/>
<img width="703" height="154" alt="Screenshot 2026-06-08 at 5 31 36 PM"
src="https://github.com/user-attachments/assets/983088e4-3265-4251-80b1-bdfcdc0e956d"
/>

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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.


[SWAPS-4561]:
https://consensyssoftware.atlassian.net/browse/SWAPS-4561?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches quote fetching and analytics context across Unified
Swap/Bridge, Batch Sell, and Quick Buy; behavior should be unchanged
aside from attribution, but a wrong or missing id could break typing or
mislabel metrics after the controller API change.
> 
> **Overview**
> Bumps **`@metamask/bridge-controller`** (and related lockfile entries)
so quote and analytics APIs can require a **`FeatureId`**, then threads
that id through swap/bridge, batch sell, and Quick Buy.
> 
> **Unified Swap/Bridge** adds `feature_id:
FeatureId.UNIFIED_SWAP_BRIDGE` to shared quote context
(`useUnifiedSwapBridgeContext`) and to client
`trackUnifiedSwapBridgeEvent` payloads (quotes sorted, gasless keypad
input, token asset tooltip). **Batch Sell** adds `feature_id:
FeatureId.BATCH_SELL` on batch quote request context.
> 
> **Quick Buy** introduces `getQuickBuyFeatureId` to map sheet entry
`source` to `QUICK_BUY_TOKEN_DETAILS` vs `QUICK_BUY_FOLLOW_TRADING`,
passes analytics `source` from `useQuickBuyController`, and calls
`BridgeController.fetchQuotes(params, featureId, signal)` instead of a
string literal. Unit tests are updated across these flows, including
abort-signal argument index changes for the new `fetchQuotes` signature.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d06c3e0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **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?
-->

Product analytics need visibility into how users enter and complete the
Money Account ↔ Card linkage flow. Today, linkage can start from several
surfaces (Money Home action row, onboarding card, MetaMask card section,
Card Home, Spending Limit promo, link sheet) but those paths were not
consistently attributed in MetaMetrics.

This PR instruments the full linkage funnel:

- **View and click events** (`CARD_VIEWED`, `CARD_BUTTON_CLICKED`) on
Money Home, Card Home, onboarding card, MetaMask card, spend-and-earn
promo, and link confirmation sheet — each tagged with `screen`,
`entrypoint`, `flow`, and contextual properties (`card_state`,
`card_type`, `mode`, `action`).
- **Lifecycle events** (`CARD_MONEY_ACCOUNT_LINKING_STARTED`,
`COMPLETED`, `FAILED`) in `useMoneyAccountCardLinkage`, including
failure reasons and revoke detection.
- **Entry point propagation** through navigation params and a typed
Redux pending state (`pendingMoneyAccountCardLink: CardEntryPoint |
null`) so the origin entry point survives the post-authentication resume
path. The link sheet reports both `entrypoint` (sheet) and
`origin_entrypoint` (where the user started).

New enums in `app/components/UI/Card/util/metrics.ts`: `CardEntryPoint`,
`CardFlow`, `CardLinkingFailureReason`, plus additional `CardScreens`
and `CardActions` values.

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

## **Manual testing steps**

```gherkin
Feature: Money Account Card linkage analytics

  Background:
    Given I am logged into MetaMask Mobile
    And I have a Money Account with a positive balance
    And the Card feature is enabled for my region

  Scenario: user starts linkage from Money Home MetaMask card
    Given I am on the Money Home screen
    And I see the MetaMask Card section in link mode

    When user taps "Link card"
    Then the link confirmation sheet should open
    And confirming should start the linkage flow

  Scenario: user starts linkage from Money onboarding card step
    Given I am on the Money Home screen
    And the onboarding card is visible on step 2 (Get a card)

    When user taps the primary CTA on the onboarding card
    Then the app should route into the card linkage flow
    And the flow should resume after authentication if required

  Scenario: user starts linkage from Card Home money account card
    Given I am a cardholder on Card Home
    And the Money Account card section is shown in link mode

    When user taps "Link card" on the money account card
    Then the link confirmation sheet should open

  Scenario: user starts linkage from Spending Limit spend-and-earn promo
    Given I am on the Card Spending Limit screen
    And the spend-and-earn promo is visible

    When user taps "Use Money account"
    Then the Money Account should be selected as the spending source

  Scenario: user confirms linkage in the link card sheet
    Given the link confirmation sheet is open
    And the sheet was opened from a tracked entry point

    When user taps the confirm CTA
    Then the sheet should dismiss
    And the linkage should proceed in the background with success or error toast feedback

  Scenario: user cancels linkage in the link card sheet
    Given the link confirmation sheet is open

    When user taps the close button
    Then the sheet should dismiss
    And no on-chain linkage should be initiated
```

## **Screenshots/Recordings**

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

### **Before**

N/A

### **After**

N/A

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

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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]
> **Medium Risk**
> Touches post-auth linkage resume Redux state and delegation analytics
hooks; on-chain behavior is unchanged, but mis-attributed entrypoints
could skew funnel data.
> 
> **Overview**
> Adds **MetaMetrics attribution** for the Money Account ↔ Card linkage
funnel across Money Home, Card Home, onboarding, spend-and-earn promo,
and the link confirmation sheet.
> 
> **View/click events** (`CARD_VIEWED`, `CARD_BUTTON_CLICKED`) are wired
with shared `screen`, `entrypoint`, `flow`, and context (`card_state`,
`mode`, `card_type`, `action`). Impressions are **gated** until card
home data is settled (`analyticsReady` / `selectCardHomeDataStatus`).
> 
> **Linkage lifecycle** events (`CARD_MONEY_ACCOUNT_LINKING_STARTED`,
`COMPLETED`, `FAILED`) run in `useMoneyAccountCardLinkage`, including
failure reasons, revoke detection, and balancing failed events when
linkage aborts mid-flight.
> 
> **Entry point propagation** replaces the boolean
`pendingMoneyAccountCardLink` with `CardEntryPoint | null`, passes
`entrypoint` through `startLinkFlow`, link sheet route params, and
`confirmLinkInBackground`; the sheet reports `origin_entrypoint` vs
sheet `entrypoint`. Pending resume state is **excluded from
redux-persist**.
> 
> New taxonomy lives in `metrics.ts` (`CardEntryPoint`, `CardFlow`,
`CardLinkingFailureReason`, `deriveCardState`).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f059ed1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…o prevent Earn onboarding crash (#31475)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

Removed the global `RiveRenderer.defaultRenderer(...,
RiveRendererAndroid.Canvas)` call in `FoxLoader` during app init.
Although this lived in the splash-screen loader, `defaultRenderer` is a
process-wide setting that forced every Rive view in the app onto
Android's Canvas renderer `Surface.lockCanvas path`. This caused the
Money/Earn onboarding stepper to hard-crash `SIGABRT`/`SIGSEGV` on
Android when the Rive view tore down during tab navigation, the worker
thread called `lockCanvas` on an already-released Surface, leaving a
pending JNI exception that ART aborted on. Tombstone confirmed on Pixel
6a / Android 16. Reverting to the default Rive GPU renderer removes the
`lockCanvas` code path entirely.

iOS is unaffected (already uses the GPU renderer).

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

<!-- mms-check: type=changelog required=true -->

<!--
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: Fixed an Android crash when entering Earn onboarding
caused by the Rive Canvas renderer being forced app-wide.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: #31166
#31167

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Earn onboarding no longer crashes on Android

  Scenario: User enters Earn onboarding on Android after fresh install
    Given the app is built in release mode on a Pixel 6a (or equivalent) running Android 16
    And the user has completed initial app load (FoxLoader splash animation plays)

    When the user taps the Money/Earn tab
    And the Rive onboarding stepper mounts
    Then the app does not crash
    And the onboarding animation renders and steps through normally

  Scenario: Splash animation still plays on Android
    Given a fresh launch on Android
    When the FoxLoader splash screen appears
    Then the fox Rive animation plays to completion without visual regressions
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->


https://github.com/user-attachments/assets/32c75ebd-8a93-4afb-ae8b-250fe4540b4f

### **Before**

### Crash on open


https://github.com/user-attachments/assets/75339490-e41f-4c42-9853-c6bce7baaffc

### Crash on close


https://github.com/user-attachments/assets/8fe061e9-6e5d-4c21-8d46-06082783de90

### **After**


https://github.com/user-attachments/assets/32c75ebd-8a93-4afb-ae8b-250fe4540b4f

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
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)

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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`.
-->

- [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**
> Small splash-only change with process-wide rendering impact on
Android; fixes a production crash but may reintroduce any original
Canvas-renderer motivation (e.g. splash geometry).
> 
> **Overview**
> Removes the **Android-only** `RiveRenderer.defaultRenderer(...,
RiveRendererAndroid.Canvas)` setup from `FoxLoader` so Rive is no longer
forced app-wide onto the Canvas renderer during splash init.
> 
> That global override affected every Rive view (not just the fox
splash), which led to **SIGABRT/SIGSEGV** when Earn/Money onboarding
Rive UI tore down during tab navigation (`lockCanvas` on a released
Surface). **iOS is unchanged**; Android again uses Rive’s default GPU
renderer.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a212356. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…31489)

## **Description**

Part of parent epic **PRED-834 — IA & Navigation Overhaul**.

**Reason for the change**
The Predict home redesign introduces multiple config-driven feeds
(sports, politics, crypto, live, trending, popular-today). Until now
there was no single generic route to render an arbitrary feed, and
`handlePredictUrl` only knew about market detail links and the dedicated
World Cup flow. We need one reusable route plus deeplink support so
`feed=<id>` links open the correct feed and so home sections can
navigate to a full feed via their "See all" affordance.

**What changed**
- Added a generic, typed route `Routes.PREDICT.FEED` and registered its
`Stack.Screen` (rendered by the existing config-driven
`PredictFeedView`).
- Extended `handlePredictUrl` so that:
- `feed=world-cup` continues to route to the dedicated World Cup flow
(unchanged).
- Known generic feed IDs route to `PredictFeedView` (gated behind the
`predictHomeRedesign` flag).
- Unknown feeds and the flag-off case fall back gracefully to the
Predict home/market list.
- `tab`, `filter`, and `q`/`query` are parsed; `filter` is parsed
separately from `tab` (`tab -> initialTabId`, `filter ->
initialFilterId`); `utm_source` and existing `market`/`marketId` links
are preserved.
- Wired the Live Now section "See all" chevron to navigate to the
generic feed (`feedId: 'live'`), establishing the reference pattern for
the other home sections.
- Refactored the deeplink handler's raw tab field from the
World-Cup-specific `worldCupTab` to a neutral `rawTab` shared by both
the World Cup and generic-feed paths (decoupling).
- Updated `docs/readme/deeplinking.md` to document the now-supported
predict params.

**Constraints / trade-offs**
- Generic feed deeplinks are intentionally flag-gated on
`predictHomeRedesign`; while the flag is off they fall back to the
market list, so nothing is exposed prematurely during rollout.

## **Changelog**

<!-- Feature is gated behind the `predictHomeRedesign` flag and is part
of the larger, not-yet-released home redesign epic. -->

CHANGELOG entry: null

## **Related issues**

Refs: PRED-834
Fixes: <!-- TODO: add the GitHub issue / ticket number for this sub-task
-->

## **Manual testing steps**

> Generic-feed deeplinks require the `predictHomeRedesign` feature flag
to be enabled. World Cup and market links work regardless of the flag.

```gherkin
Feature: Generic Predict feed route and deeplinks

  Scenario: Known generic feed deeplink opens PredictFeedView (flag on)
    Given the predictHomeRedesign flag is enabled
    When the user opens the deeplink "/predict?feed=sports&tab=all&filter=live"
    Then the app opens PredictFeedView for the sports feed
    And the "all" tab and the "live" filter are pre-selected

  Scenario: Search deeplink opens the feed with the search overlay
    Given the predictHomeRedesign flag is enabled
    When the user opens the deeplink "/predict?feed=trending&q=bitcoin"
    Then the app opens PredictFeedView for the trending feed
    And the search is pre-populated with "bitcoin"

  Scenario: World Cup deeplink is unchanged
    When the user opens the deeplink "/predict?feed=world-cup&tab=live"
    Then the app opens the dedicated World Cup flow on the live tab

  Scenario: Unknown feed falls back gracefully
    Given the predictHomeRedesign flag is enabled
    When the user opens the deeplink "/predict?feed=not-a-real-feed"
    Then the app falls back to the Predict home/market list

  Scenario: Generic feed falls back when the flag is off
    Given the predictHomeRedesign flag is disabled
    When the user opens the deeplink "/predict?feed=sports"
    Then the app falls back to the Predict home/market list

  Scenario: Existing market deeplink still works
    When the user opens the deeplink "/predict?market=23246&utm_source=test"
    Then the app opens the market details for that market

  Scenario: Live Now "See all" navigates to the generic feed
    Given the predictHomeRedesign flag is enabled and the Predict home is visible
    When the user taps "See all" on the Live Now section
    Then the app opens PredictFeedView for the "live" feed
```

## **Screenshots/Recordings**

### **Before**

<!-- TODO: recording of feed= deeplink falling back to market list, and
Live Now "See all" non-interactive -->

### **After**

<!-- TODO: recording of feed= deeplinks opening PredictFeedView with
tab/filter/search applied, and Live Now "See all" navigating to the live
feed -->



https://github.com/user-attachments/assets/043c5453-6038-4352-9d0f-149ccdb1fcdc



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

- [x] I've tested on Android
- [x] I've tested with a power user scenario
- [x] I've instrumented key operations with Sentry traces for production
performance metrics

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

---

## Files changed

| File | Change |
| --- | --- |
| `app/constants/navigation/Routes.ts` | Added `PREDICT.FEED:
'PredictFeed'` route constant |
| `app/components/UI/Predict/routes/index.tsx` | Registered the
`PredictFeedView` `Stack.Screen` for `Routes.PREDICT.FEED` |
| `app/core/DeeplinkManager/handlers/legacy/handlePredictUrl.ts` | Parse
`filter` separately; flag-gated generic-feed routing; `worldCupTab` ->
`rawTab`; updated TSDoc |
|
`app/components/UI/Predict/views/PredictHome/components/PredictLiveNowSection/PredictLiveNowSection.tsx`
| Wired "See all" to navigate to the generic feed (`feedId: 'live'`) |
| `docs/readme/deeplinking.md` | Documented predict params
(`feed`/`tab`/`filter`/`q`/`query`) |
|
`app/core/DeeplinkManager/handlers/legacy/__tests__/handlePredictUrl.test.ts`
| Generic-feed, tab/filter, fallback, query, and World Cup compatibility
tests |
| `app/components/UI/Predict/routes/index.test.tsx` |
`Routes.PREDICT.FEED` registration test |
| `app/components/UI/Predict/views/.../PredictLiveNowSection.test.tsx` |
"See all" navigation test |

## Acceptance criteria coverage

- [x] Generic feed route is typed and registered
- [x] Deeplinks with known generic feed IDs open `PredictFeedView`
- [x] `filter` query param is parsed separately from `tab`
- [x] `feed=world-cup` still opens the dedicated World Cup flow
- [x] Unknown feeds gracefully fall back to Predict home/market list
- [x] Existing query/search and market deeplinks still work
- [x] Tests updated (`handlePredictUrl.test.ts`, route registration);
manual QA pending recordings

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches deeplink routing and entry-point analytics for Predict;
behavior is gated behind `predictHomeRedesign` with market-list
fallbacks, but incorrect feed/tab mapping could misroute users from
external links.
> 
> **Overview**
> Adds a **generic Predict feed** navigation path so config-driven feeds
(sports, live, trending, etc.) can open in one screen, including from
deeplinks and home “See all” actions.
> 
> **Navigation:** New `Routes.PREDICT.FEED` registers `PredictFeedView`
on the Predict stack. **Live Now** on Predict home is no longer a static
header—the section header is pressable and navigates to
`PredictFeedView` with `feedId: 'live'` and home-section analytics.
> 
> **Deeplinks (`handlePredictUrl`):** Parses `filter` separately from
`tab` (`tab` → `initialTabId`, `filter` → `initialFilterId`). When
`predictHomeRedesign` is on and `feed` is a known generic id, navigation
goes to `PredictFeedView` (with optional `q`/`query` and `utm_source` on
`entryPoint`). `feed=world-cup` still uses the World Cup flow; unknown
feeds or flag-off fall back to the market list; `market` still wins over
`feed`. Deeplink docs list the new predict query params.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7353b40. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Integrates Appium smoke tests into CI alongside existing Detox smoke
jobs.

- Adds **Playwright + Appium** smoke runner (`tests/smoke-appium/`,
`tests/playwright.smoke-appium.config.ts`) with **SmokeAccounts** tag
selection aligned to smart E2E.
- **PR CI (Android only):** `appium-smoke-gate` +
`appium-smoke-tests-android` in `ci.yml` after `build-android-apks`
(Namespace `metamask-android-build` profile). Respects smart E2E tag
selection via `scripts/e2e/appium-smoke-tags.mjs`.
- **Main regression (iOS):** runs on `main` every 6 hours (4×/day).
Builds iOS E2E artifact, prebuilds WDA in parallel, then runs
`run-appium-smoke-tests-ios.yml` with all smoke tags.
- iOS runner prep (`prepare-ios-appium-runner.mjs`): sim boot, WDA
prebuild/cache, `simctl` install, optional WDA warm-up. Failure screen
recordings, JUnit/HTML reports on both platforms.
- Extends the Playwright/Appium framework: local emulator/sim boot,
Appium server reuse (`SKIP_APPIUM_STOP`), Android cold-boot
stabilization (AOSP image, ANR handling), iOS WDA preinstall path,
encapsulated page-object branches for shared Detox flows.
- Detox Jest config ignores `tests/smoke-appium/`; Appium smoke jobs are
**not** merge-blocking (`check-all-jobs-pass` excludes them).

Evidence of video recording working:


https://github.com/user-attachments/assets/259296b5-aa48-4294-85a9-bb4ad312ae14


https://github.com/user-attachments/assets/37585d75-c37b-4bb8-af8b-8529fe26e505

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Refs: N/A — CI / test infrastructure only (no product ticket).

## **Manual testing steps**

```gherkin
Feature: Appium smoke CI

 Scenario: SmokeAccounts SRP reveal runs on Android PR CI
 Given a PR triggers CI with Appium smoke enabled
 When the Android Appium smoke job runs on the Namespace runner
 Then the SmokeAccounts SRP reveal spec passes (or retries then passes)
 And failure screen recordings are uploaded when a test fails

 Scenario: SmokeAccounts SRP reveal runs on iOS scheduled regression
 Given main has run-appium-smoke-tests-ios-scheduled.yml
 When the scheduled workflow runs (or is triggered via workflow_dispatch)
 Then the iOS build, WDA prebuild, and SmokeAccounts spec complete
 And failure screen recordings are uploaded when a test fails
```

Local: `yarn appium-smoke:android` / `yarn appium-smoke:ios` with a
debug E2E build.

Manual iOS regression: **Actions → Appium Smoke Tests (iOS — main
regression) → Run workflow** (after merge to `main`).

## **Screenshots/Recordings**

N/A — CI failure screen recordings are uploaded as workflow artifacts
(`appium-smoke-videos-*`) when a test fails.

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

- [x] I've tested on Android
 - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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**

- [ ] 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**
> Large CI and test-infrastructure change (new workflows, emulator/WDA
paths) with limited product code impact; Appium smoke is gated and
non-merge-blocking, but failures could add CI noise and runner cost.
> 
> **Overview**
> Adds **Playwright + Appium** mobile smoke testing to CI and the local
dev workflow, parallel to existing Detox smoke jobs.
> 
> **CI:** PR pipeline gets `appium-smoke-gate` (via
`appium-smoke-tags.mjs` + smart E2E tags) and **Android**
`appium-smoke-tests-android` after APK build. New reusable workflows run
Playwright with `tests/playwright.smoke-appium.config.ts`, shard by
smoke tag (initially **SmokeAccounts**), and publish JUnit/HTML plus
failure videos. **iOS** runs on a **6-hour schedule** on `main` with
build + WDA prebuild (`prebuild-wda-ios.yml`,
`prepare-ios-appium-runner.mjs`) before tests. `setup-e2e-env` gains
optional `install-applesimutils` (off for Appium) and fixes Android SDK
`chown` for `sdkmanager`.
> 
> **Runner / framework:** Implements local emulator/sim boot, Android
cold-boot stabilization (ANR handling, package trimming), Appium server
reuse (`SKIP_APPIUM_STOP`), iOS WDA cache/preinstall, failure screen
recording, and cross-framework gestures/assertions in shared page
objects and flows. Detox Jest ignores `tests/smoke-appium/`; Appium jobs
are **not** merge-blocking in `check-all-jobs-pass`.
> 
> **Local:** `yarn appium-smoke:android` / `yarn appium-smoke:ios`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e98dd70. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cmd-ob <cmd-ob@users.noreply.github.com>
…tionDetails to HeaderStandard (#31443)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->
1. Migrates ConfirmAddAsset and DeFiProtocolPositionDetails from React
Navigation stack headers to in-screen HeaderStandard components,
aligning them with the ongoing header migration pattern.
2. Fixes ActivityView safe-area handling so the header no longer shifts
when switching between back-button and default header states.


<!--
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?
3. What is the improvement/solution?
-->

## **Changelog**

<!-- mms-check: type=changelog required=true -->

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

<!-- mms-check: type=issue-link required=true -->

Fixes: Migrate ConfirmAddAsset and DeFiProtocolPositionDetails screens
to HeaderStandard

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: HeaderStandard migration
  
Scenario: Confirm add asset shows header and back works
    Given the user is on the confirm add asset screen
    Then the add asset title is displayed in the header
    When the user taps the back button
    Then the user returns to the previous screen
  
Scenario: Confirm add asset import still works
    Given the user is on the confirm add asset screen with tokens selected
    When the user taps import
    Then the user is navigated to the wallet screen
  
Scenario: DeFi protocol position details shows back button and content
    Given the user is on the DeFi protocol position details screen
    Then the protocol name is displayed
    And a back button is displayed
    When the user taps the back button
    Then the user returns to the wallet screen
  
Scenario: Activity header is stable and respects safe area
    Given the user is on the Activity screen
    Then the activity title is displayed in the header
    And the header does not shift on load
 
Scenario: Activity back button navigates home when Money account is enabled
    Given the Money account feature is enabled
    And the user is on the Activity screen
    When the user taps the back button
    Then the user is navigated to the home screen
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
iOS
|BEFORE|AFTER|
|---|---|
|<img width="366" height="752" alt="Before"
src="https://github.com/user-attachments/assets/ef0d7359-0c50-4409-8931-98e227493cfa"
/>|<img width="366" height="752" alt="After"
src="https://github.com/user-attachments/assets/54356e8c-c515-4fa7-9d2c-260aa68201fa"
/>|

Android

https://github.com/user-attachments/assets/7fa36b5d-ca9d-404a-b680-372aaeb5ca32


## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
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)

- [x] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [x] 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
- [x] 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`.
-->

- [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]
> **Low Risk**
> UI and navigation presentation only; no auth, transactions, or
data-layer changes, with unit test coverage for back navigation and safe
areas.
> 
> **Overview**
> Continues the **HeaderStandard** migration by hiding stack headers for
**ConfirmAddAsset** and **DeFiProtocolPositionDetails** and rendering
headers inside each screen instead.
> 
> **ConfirmAddAsset** drops `navigation.setOptions` /
`getHeaderCompactStandardNavbarOptions` and adds an in-screen
`HeaderStandard` with the add-asset title and `goBack`, wrapped in
`SafeAreaView` with left/right/bottom edges only.
> 
> **DeFiProtocolPositionDetails** replaces the stack navbar
(`getDeFiProtocolPositionDetailsNavbarOptions`) with `HeaderStandard`
(empty title, `navigation.pop` on back), `SafeAreaView` +
`includesTopInset`, and a content wrapper style; the removed navbar
helper and its tests are deleted from `Navbar`.
> 
> **ActivityView** changes `SafeAreaView` from top additive edges to
left/right/bottom only and sets **`includesTopInset`** on both
`HeaderStandard` and `HeaderRoot` so top inset is handled by the header
and the title no longer jumps when toggling back vs default header.
> 
> Navigator and component tests are updated for `headerShown: false` and
the new back/safe-area behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0170d11. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@pull pull Bot locked and limited conversation to collaborators Jun 10, 2026
@pull pull Bot added the ⤵️ pull label Jun 10, 2026
@pull
pull Bot merged commit 0356eb4 into Reality2byte:main Jun 10, 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.

9 participants