Skip to content

[pull] main from MetaMask:main#823

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

[pull] main from MetaMask:main#823
pull[bot] merged 15 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 : )

ccharly and others added 15 commits June 10, 2026 12:03
## **Description**


Integrating our new `SnapAccountService` before we start migrating the
old `SnapKeyring` with the new `SnapKeyringV2`.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: N/A

## **Manual testing steps**

Not applicable

## **Screenshots/Recordings**

Not applicable

## **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]
> **High Risk**
> Touches snap keyring creation, permission RPC keyring hooks, and
multichain routing—security-sensitive paths where regressions could
break snap accounts or deadlock keyring access.
> 
> **Overview**
> Wires **`@metamask/snap-account-service`** into the Engine as a
first-class context module (init, scoped messenger, stateless
registration) so snap account lifecycle and keyring messaging can live
in one place ahead of **SnapKeyringV2**.
> 
> **Snap keyring access** no longer duplicates
`KeyringController:getKeyringsByType` / `addNewKeyring` in Engine,
multichain routing, or snap permission specs. Callers use
**`SnapAccountService:getLegacySnapKeyring`** and
**`handleKeyringSnapMessage`** instead; **`AccountsController`** now
listens for **`SnapAccountService:*`** account update events rather than
legacy SnapKeyring constants.
> 
> **Onboarding gating** for snaps is extracted to shared
**`ensureOnboardingComplete`** (used by SnapController and
SnapAccountService init). **Mnemonic / keyring** paths in snap
permissions move to **`withKeyringV2Unsafe`** and v2 HD/Ledger types.
Dependency bumps include **`accounts-controller`**,
**`multichain-account-service`**, **`keyring-sdk`**, and new
**`snap-account-service`**, plus **tsconfig** path aliases for keyring
v2 packages.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
72523b4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…sufficient funds (#31450)

## **Description**

When selling the **entire** balance of a near-$1 source token (e.g.
mUSD) via QuickBuy's "Sell" flow, the trade was incorrectly blocked with
an **"Insufficient funds"** button state.

**Root cause:** the QuickBuy "Sell" flow is fiat-first. When the slider
is dragged to 100%, the committed amount was reconstructed through a
lossy fiat round-trip: `balance → USD (rounded to 2 decimals via
.toFixed(2)) → tokens (usd / currencyExchangeRate)`. Because the
exchange rate of these tokens is fractionally below `1`, dividing the
cent-rounded USD value back into tokens yields an amount **slightly
larger** than the actual on-chain balance (e.g. `0.10003 MUSD` balance →
`$0.10` → `0.10003001… MUSD`). `useIsInsufficientBalance` then compared
this inflated amount against the true atomic balance and flagged the
trade. The unpriced slider path had the same class of issue via
`parseFloat` reconstruction.

**Fix:** an `isMaxSourceAmount` sentinel. When the slider is committed
at 100% ("sell all"), `sourceTokenAmount` now returns the exact
`latestSourceBalance.displayBalance` (which is
`formatUnits(atomicBalance, decimals)` and therefore round-trips back to
the precise atomic balance) instead of the reconstructed value. The flag
is set at the priced commit (`handleSliderDragEnd`) and the unpriced
commit (`handleSliderChange`), and cleared on custom typed input and on
amount reset (token switch / mode flip). This fixes both the priced
(reported mUSD) and unpriced paths.

## **Changelog**

CHANGELOG entry: Fixed a bug where selling your entire balance of a
token in QuickBuy could be incorrectly blocked with an "Insufficient
funds" error.

## **Related issues**

Fixes: TSA-650

## **Manual testing steps**

```gherkin
Feature: QuickBuy sell-all does not falsely report insufficient funds

  Scenario: Selling the entire balance of a near-$1 token
    Given I hold a small balance of a near-$1 token (e.g. mUSD) whose exchange rate is fractionally below 1
    And I open the QuickBuy sheet and select the "Sell" tab
    And I have selected a token to receive (e.g. ETH)

    When I drag the amount slider all the way to 100% (sell all)
    Then the headline shows my full balance and its fiat value
    And the confirm button is enabled (NOT "Insufficient funds")
    And submitting the trade sells the exact on-chain balance

  Scenario: Selling a partial amount still works
    When I drag the slider to a value below 100%
    Then the amount is derived from the fiat value as before
    And the confirm button reflects the correct sufficient/insufficient state
```

## **Screenshots/Recordings**

### **Before**

<!-- See linked ticket: dragging the slider to 100% ("sell all") on a
near-$1 token shows "Insufficient funds" and blocks the trade. -->

### **After**

<!-- Slider at 100% spends the exact balance; the confirm button is
enabled. Verified via unit tests in useQuickBuyController.test.ts (66/66
passing, incl. 4 new cases). On-device recording N/A for this logic-only
fix. -->

N/A — logic-only fix covered by unit tests; no visual change beyond the
button no longer being erroneously disabled.

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Localized QuickBuy amount derivation and balance checks; no auth or
payment-rail changes, with new unit tests covering the regression.
> 
> **Overview**
> Fixes QuickBuy **sell-all** falsely showing **Insufficient funds**
when the slider is committed at **100%**.
> 
> The change adds an **`isMaxSourceAmount`** flag so
**`sourceTokenAmount`** uses **`latestSourceBalance.displayBalance`**
(exact on-chain balance) instead of amounts rebuilt from **cent-rounded
USD** or **float slider math**, which could exceed the real balance for
near-$1 tokens. The flag is set on **priced** slider commit
(`handleSliderDragEnd`, including **before** the USD dedup guard so
typed-max then slide-to-100% still works) and on the **unpriced** path
(`handleSliderChange`), and cleared on **reset**, **custom input**, and
non-100% slider moves. **`useLatestBalance`** is moved earlier so max
mode can use it in the memo. **Four unit tests** cover the stablecoin
round-trip scenario.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
903b931. 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 Opus 4.8 (1M context) <noreply@anthropic.com>
<!--
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?
-->

Migrate `Skeleton` component (predict team).

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

## **Manual testing steps**

```gherkin
Feature: skeleton migration

  Scenario: user checks prediction
    Given the user on prediction page

    When user clicks yes for a prediction
    Then skeleton should be visible
```

## **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/82e49081-c108-4588-b972-df29cc8acc48"
/>

### **After**

<img width="1206" height="2622" alt="image"
src="https://github.com/user-attachments/assets/f41fa30c-1245-4114-9340-b9b9799f6130"
/>

## **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]
> **Low Risk**
> Import-only design-system path updates with no behavior changes; low
regression risk beyond loading UI appearance if the temp Skeleton API
differs.
> 
> **Overview**
> Updates Predict buy flow UI to import **Skeleton** from
`component-library/components-temp/Skeleton` instead of the legacy
`components/Skeleton` path.
> 
> **PredictBuyAmountSection** and **PredictFeeSummary** keep the same
loading placeholders; only the module path changes. The
**PredictBuyAmountSection** test mock is updated to match the new
import.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
20d9b1d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

The transfer button on the Money home view used to be tappable even when
the account had no spendable balance, letting people start a transfer
they couldn't complete. It's now disabled until there's a real,
spendable balance.

"Spendable" means a balance value is loaded and is at least one cent —
sub-cent dust no longer counts as funded, since mUSD is USD-pegged and
anything below a cent is economically meaningless. This same threshold
now drives the funded/unfunded state, so the earnings, how-it-works, and
what-you-get sections stay consistent with the button and with the Money
balance displayed to the user.

`MoneyActionButtonRow` was reworked to take per-button config objects
(`add`/`transfer`/`card`) so individual buttons can be disabled, and the
now-unused `isAccountFunded` util was removed.
<!-- 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: disable the transfer button on the Money Home screen
when Money account does not have spendable balance.

## **Related issues**

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

Fixes:
[MUSD-946](https://consensyssoftware.atlassian.net/browse/MUSD-946)

## **Manual testing steps**

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

```gherkin
Feature: Money transfer button availability

  Scenario: user with an empty balance cannot transfer
    Given a Money account with a zero or sub-cent balance
    When user opens the Money home view
    Then the transfer button is disabled

  Scenario: user with a funded balance can transfer
    Given a Money account with a balance of at least one cent
    When user opens the Money home view
    Then the transfer button is enabled
    And user can start a transfer
```

## **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 - Transfer button was enabled
### **After**

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


https://github.com/user-attachments/assets/2c0a9534-a701-4ab1-af06-ad7204a5f6cc

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

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


[MUSD-946]:
https://consensyssoftware.atlassian.net/browse/MUSD-946?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes Money home funded/unfunded gating and transfer entry across
several balance edge cases; behavior is well covered by new tests but
affects a primary user flow.
> 
> **Overview**
> Disables **Transfer** on Money Home until the account has a
**spendable** balance: loaded fiat raw amount at least **one cent**
(`DUST_THRESHOLD`), replacing `tokenTotal` / `isAccountFunded` with
`hasSpendableBalance` and `hasBalanceValue` for funded vs unfunded UI.
> 
> **Transfer** is wired through `MoneyActionButtonRow` with per-button
`{ onPress, disabled? }` configs and `ButtonBase` `isDisabled`, so zero,
loading, unavailable-rate, and sub-cent dust balances cannot open the
transfer sheet. **MoneyEarnings** and unfunded onboarding (**How it
works**, **What you get**) follow the same rules; unavailable balance no
longer shows empty-account onboarding.
> 
> Exports `DUST_THRESHOLD` from `moneyFormatFiat` and removes
`isAccountFunded`. Tests cover dust, unavailable, disabled transfer, and
regression cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d4df2d0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…he sold token (#31469)

## **Description**

When selling a token in QuickBuy, the **receive** token list could
contain — and default to — the **same token being sold**, which is a
no-op (e.g. selling USDC on Base offered/defaulted to receiving USDC).

The receive options are built stablecoins-first and sorted with the
position's chain first, so the previous logic — which simply preselected
`sellDestTokenOptions[0]` — landed on the very token being sold whenever
the position was a stablecoin that also appears in the curated
stablecoin list. The token was also still selectable in the picker.

This PR fixes both the default and the list:

- **Excludes the token being sold from the receive list entirely**, so
it can't be selected at all (receiving the same token you're selling is
a no-op).
- Introduces `selectDefaultReceiveToken`, which prefers the **native
token of the sold token's chain** as the default (e.g. selling USDC on
Base now defaults to ETH on Base), falling back to the first remaining
candidate when the user is already selling the native token or when no
native option is available.

The auto-select effect is gated on `!isSetupLoading` and reads the sold
token's identity from the normalised `positionTokenFromSetup` (rather
than the balance-enriched `positionToken`), so both the exclusion and
the default are correct even before the balance resolves — closing a
race where the default could still be set to the sold token.

<img width="973" height="1066" alt="image"
src="https://github.com/user-attachments/assets/48caf26e-1e30-44e3-8919-d47b5c9e9c69"
/>


## **Changelog**

CHANGELOG entry: Fixed QuickBuy so selling a token no longer lists or
defaults the receive token to the same token; the sold token is removed
from the receive options and the default is now the chain's native token
(e.g. selling USDC on Base defaults to ETH).

## **Related issues**

Fixes: N/A — internal bug report (QuickBuy sell default/listing of
receive token).

## **Manual testing steps**

```gherkin
Feature: QuickBuy sell receive token

  Scenario: The token being sold is not offered as a receive option
    Given I hold USDC on Base
    And I open the QuickBuy sheet for that position
    When I switch to "Sell" mode
    And I open the "Receive" token picker
    Then USDC on Base is not listed as a receive option

  Scenario: Selling a stablecoin defaults to the chain native token
    Given I hold USDC on Base
    And I open the QuickBuy sheet for that position
    When I switch to "Sell" mode
    Then the default "Receive" token is ETH on Base
    And it is never USDC (the token being sold)

  Scenario: Selling the native token defaults to a stablecoin
    Given I hold ETH on Base
    And I open the QuickBuy sheet for that position
    When I switch to "Sell" mode
    Then the default "Receive" token is a stablecoin on Base (not ETH)
```

## **Screenshots/Recordings**

### **Before**

<!-- Selling USDC on Base listed and defaulted the receive token to
USDC. -->

N/A — logic-only change; behavior covered by unit tests.

### **After**

<!-- Selling USDC on Base removes USDC from the receive list and
defaults to ETH. -->

N/A — logic-only change; behavior covered by unit tests.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.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**

Initial implementation of the UI messenger. This sets up the UI
messenger, a route messenger, and refactors a Snap page to demonstrate
how to use the new route-level messenger.

More details can be found in [the UI messenger
ADR](https://github.com/MetaMask/decisions/blob/6760a1616df98f5b8bb7c876fadd7f255a2b8957/decisions/core/0020-integrate-messengers-into-ui.md).

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

## **Related issues**

Refs: n/a

## **Manual testing steps**

n/a

## **Screenshots/Recordings**

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**
> Changes how UI invokes controller actions (snap removal now goes
through delegation) and introduces a broad new access-control surface;
mitigated by explicit per-route capabilities and KeyringController
exclusions, but more routes will need careful capability review as
adoption grows.
> 
> **Overview**
> Introduces a **UI messenger layer** so screens can call controller
actions through typed, capability-scoped messengers instead of reaching
into `Engine` directly.
> 
> **Root** now creates a singleton `UIMessenger` and wraps the app in
`UIMessengerProvider`. The UI messenger delegates allowed actions to
`Engine.controllerMessenger` and forwards events from the background; a
config list (starting with **KeyringController** unsafe/keyring APIs)
blocks actions that must not be exposed to the UI.
> 
> **Per-route messengers** are wired via `withMessenger` /
`RouteWithMessenger`: on mount they delegate only the declared
actions/events, expose them through `RouteMessengerContext`, and revoke
on unmount. **Snap Settings** is the first adopter—the navigator wraps
`SnapSettings` with `SnapController:removeSnap`, and removal uses
`useMessenger().call('SnapController:removeSnap', …)` instead of
`Engine.context.SnapController.removeSnap`. Tests and
`renderWithProvider` gain optional `uiMessenger` / `routeMessenger`
mocks to match this pattern.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
dbbcad3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…31466)

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

update @metamask/transaction-pay-controller to version 23.5.0

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

## **Related issues**

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

Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1520

## **Manual testing steps**
NA

## **Screenshots/Recordings**
NA

## **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]
> **Medium Risk**
> Touches bridge/swap submission paths via controller upgrades and type
assertions on quote payloads; regression risk is moderate though changes
are mostly dependency-driven compile fixes.
> 
> **Overview**
> Bumps **`@metamask/transaction-pay-controller`** from **^23.3.0** to
**^23.5.0** and refreshes **`yarn.lock`**, pulling newer bridge stack
packages (e.g. **`bridge-controller` ~75**, **`bridge-status-controller`
72.1.0**) and related controller bumps such as **`keyring-controller`
^27**.
> 
> To satisfy TypeScript after those nested **`bridge-controller`**
copies diverge, **`useSubmitBridgeTx`** and **`useSubmitBatchSellTx`**
cast **`QuoteResponse`** / quote payloads to
**`BridgeStatusController`** parameter types, with comments explaining
the v74 vs v75 nominal type mismatch.
> 
> Test/fixture updates rename the Monad network label from **"Monad
Mainnet"** to **"Monad"** in **`initial-background-state.json`** and
**`AddressSelector.test.tsx`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1ba055e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…cp-7.81.0 (#31436)

## **Description**

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

This PR makes the Predict **game details** screen
(`PredictGameDetailsContent`) behave like the standard
`PredictMarketDetails` screen when switching between outcome-group chips
(game lines / exact score / halftime) or the Positions/Outcomes tabs. It
fixes two visual issues:

1. **Screen jumping on tab/chip change.** The component drove an
imperative `scrollTo` on every chip selection (plus a follow-up
`InteractionManager.runAfterInteractions` pass) to force the sticky
header to the top, propped up by a `windowHeight`-based `minHeight` on
the tab content. This caused the screen to jump downward on every
switch. The regular market details screen never does this — it relies
solely on the `ScrollView`'s `stickyHeaderIndices`. Removed the manual
scroll machinery (`scrollRef`, `stickyHeaderY`, `pendingChipScroll`, the
layout handler, and the effect) and the `minHeight` wrapper, so
switching chips/tabs now only swaps the content below the sticky header
and preserves scroll position.

2. **Chart overlapping the outcomes.** `PredictGameChartContent` used
`flex-1` on its loaded/error/empty containers. Inside a `ScrollView`,
`flex-1` collapses to zero height, so the fixed 200px chart `View`
overflowed downward onto the chip bar and the first outcome card.
Switched the containers to `w-full` so they size to their content —
matching the market details chart (`PredictDetailsChart`), which never
used `flex-1`.

Both changes are layout-only. Existing unit tests for `PredictGameChart`
and `PredictGameDetailsContent` (218 tests) pass unchanged, including
the `reserves chart height only while loading` test.

## **Changelog**

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

CHANGELOG entry: Fixed the Predict sports game details screen jumping
and the price chart overlapping the outcomes when switching between
markets

## **Related issues**

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

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-954

## **Manual testing steps**

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

```gherkin
Feature: Predict game details market/tab switching

  Scenario: Switching outcome-group chips does not jump or overlap the chart
    Given I open a sports game market with extended outcomes (e.g. game lines, exact score, halftime)
    And the price chart has loaded above the chip bar

    When I tap a different chip (e.g. "Exact Score")
    Then the screen does not jump to pin the chip bar to the top
    And the chart keeps its height and does not overlap the outcomes list
    And the outcomes for the selected chip render below the chip bar

  Scenario: Switching Positions/Outcomes tabs preserves scroll position
    Given I open a sports game market that shows the Positions/Outcomes tab bar

    When I switch between the Positions and Outcomes tabs
    Then the scroll position is preserved (no downward jump)
    And the chart does not overlap any content
```

## **Screenshots/Recordings**

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

### **Before**

<!-- Attach the recording showing the screen jumping on tab/chip change
and the chart overlapping the first outcome card (e.g. "Mexico 0 - 3
South Africa"). -->

### **After**


https://github.com/user-attachments/assets/ce638894-f133-40ce-89c8-77b4d38868cc

<!-- Attach a recording switching between chips/tabs with no downward
jump and no chart overlap. -->

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Layout-only Tailwind and scroll wiring changes in Predict game details
UI with no auth, data, or business-logic impact.
> 
> **Overview**
> Fixes Predict **game details** layout when users switch outcome chips
or Positions/Outcomes tabs.
> 
> **Game details scroll behavior:** Removes imperative `scrollTo` on
chip change (`scrollRef`, `stickyHeaderY`, `pendingChipScroll`, layout
measurement, and `InteractionManager` follow-up) and drops the
`windowHeight`-based `minHeight` wrapper around tab content. Chip
selection now calls `handleChipSelect` directly, relying on `ScrollView`
`stickyHeaderIndices` like standard market details so scroll position is
preserved and the screen no longer jumps.
> 
> **Chart in `ScrollView`:** In `PredictGameChartContent`, replaces
`flex-1` with `w-full` on loaded, error, and empty chart containers so
height follows content instead of collapsing inside the scroll view,
preventing the fixed-height chart from overlapping the chip bar and
outcome list.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b15f4a1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…1123)

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

address comment :

#30911 (comment)
-->

## **Description**

Agentic CLI connection loading duplicated the same notification logic
already implemented in the SDK Connect V2 host application adapter
(`agenticCliLoading.ts` dispatched `showSimpleNotification` /
`hideNotificationById` directly).

This PR removes that duplicate module and routes Agentic CLI loading
through `deps.hostapp.showConnectionLoading` / `hideConnectionLoading`,
keeping a single code path for connection-loading UI.

`showConnectionLoading` now accepts optional
`ShowConnectionLoadingOptions` with `autodismissMs`. The default remains
**10s** for standard SDK Connect flows; Agentic CLI passes **15s** via
`AGENTIC_CLI_CONNECTION_LOADING_AUTODISMISS_MS` to accommodate the
longer MWP → OTP → dashboard connect sequence.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:
#30911 (comment)
## **Manual testing steps**

```gherkin
Feature: Agentic CLI connection loading

  Scenario: user connects via Agentic CLI deeplink
    Given the app is unlocked and Agentic CLI connect is available
    When user opens an Agentic CLI connect deeplink
    Then a connection-loading notification appears with the dApp name
    And the notification auto-dismisses after ~15s if the flow stalls
    And the notification is hidden when the connect flow completes or fails

  Scenario: standard SDK Connect V2 connection loading is unchanged
    Given a non–Agentic CLI SDK Connect V2 session is establishing
    When connection loading is shown via the host adapter
    Then the loading notification still auto-dismisses after ~10s by default
```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

<!-- [screenshots/recordings] -->
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]
> **Low Risk**
> UI notification plumbing refactor with no auth or data-path changes;
default 10s autodismiss behavior is preserved for non–Agentic CLI
callers.
> 
> **Overview**
> Removes the Agentic CLI–specific loading helpers in
`agenticCliLoading.ts` and routes connection loading through the shared
SDK Connect V2 **host application adapter** instead.
> 
> `showConnectionLoading` / `hideConnectionLoading` on
`IHostApplicationAdapter` now accept optional
`ShowConnectionLoadingOptions` with `autodismissMs` (default **10s**;
Agentic CLI uses exported **15s** for longer MWP → OTP → dashboard
flows). `AgenticCliMwpConnectionService` calls
`deps.hostapp.showConnectionLoading` / `hideConnectionLoading` with that
timeout rather than dispatching notifications directly. Tests were
updated accordingly and adapter coverage was added for custom
autodismiss.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
aa778c8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ED-927) (#31219)

## **Description**

The Predict search feature (search icon → full-screen overlay in the
Predict feed) currently has **zero** analytics instrumentation. No event
fires when a user opens search, types a query, or taps a result, making
it impossible to measure search engagement or assess the feature's
value.

This PR adds a single `Predict Search Interacted` event with an
`interaction_type` discriminator covering the three key moments,
following the established `PredictAnalytics` controller pattern (config
in `predictAnalyticsEvents.ts` →
`PredictAnalytics.trackSearchInteracted` → `PredictController`
pass-through, called via
`Engine.context.PredictController.trackSearchInteracted(...)`).

| `interaction_type` | Trigger | Extra properties |
| --- | --- | --- |
| `opened` | User taps the search icon | — |
| `queried` | A debounced query resolves | `search_query`,
`results_count` |
| `result_clicked` | User taps a market result card | `search_query`,
`market_id`, `market_title` |

Base properties on all three: `interaction_type`, `predict_feed_tab`
(omitted on the tab-less redesigned home), `entry_point`.

The event is instrumented on **both** search surfaces (legacy
`PredictFeed` and the redesigned `PredictHome`), which share
`PredictSearchOverlay`. `opened` fires at the search-icon call sites;
`queried` and `result_clicked` fire inside the overlay (the latter via
`PredictMarket`'s existing `onCardPress`, so navigation is unaffected).

The matching Segment schema event is added in a separate
`segment-schema` PR (see Related issues) and should land first.

## **Changelog**

CHANGELOG entry: null

<!-- Non-user-facing analytics instrumentation only. -->

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-927

Segment schema PR: Consensys/segment-schema#601

## **Manual testing steps**

```gherkin
Feature: Predict search engagement analytics

  Scenario: user opens search
    Given the user is on the Predict feed (or redesigned home)
    When the user taps the search icon
    Then a "Predict Search Interacted" event fires with interaction_type=opened
    And predict_feed_tab (feed only) and entry_point are included

  Scenario: user runs a query
    Given the search overlay is open
    When the user types a query and the debounced search resolves
    Then a "Predict Search Interacted" event fires once with interaction_type=queried
    And search_query and results_count match the resolved results

  Scenario: user taps a result
    Given search results are shown
    When the user taps a market result card
    Then a "Predict Search Interacted" event fires with interaction_type=result_clicked
    And search_query, market_id, and market_title match the tapped market
    And the user still navigates to the market details
```

Automated coverage:
- `yarn jest
app/components/UI/Predict/controllers/PredictAnalytics.test.ts` (event
name + per-`interaction_type` property mapping, incl. `results_count=0`
and omitted optional props)
- `yarn jest -c jest.config.view.js PredictFeed.view.test
PredictHome.view.test` (opened/queried/result_clicked fire with correct
properties)

## **Screenshots/Recordings**

### **Before**

N/A — no UI changes; analytics-only instrumentation.

### **After**

N/A — no UI changes; analytics-only instrumentation.

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Analytics-only instrumentation with no UI or business-logic changes;
search queries are sent to existing metrics pipelines as designed.
> 
> **Overview**
> Adds **Predict Search Interacted** analytics so search engagement can
be measured on the legacy feed and redesigned home.
> 
> A new MetaMetrics event is wired through `PredictAnalytics` /
`PredictController` with `interaction_type` values **`opened`**,
**`queried`**, and **`result_clicked`**, plus optional
`predict_feed_tab`, `entry_point`, `search_query`, `results_count`, and
market fields. **`opened`** fires when the user taps search on
`PredictFeed` or `PredictHome`; **`queried`** and **`result_clicked`**
fire from shared `PredictSearchOverlay` after debounced search completes
(once per query, with re-track after clear) and on result card press via
`onCardPress`.
> 
> `results_count` uses the API **`totalResults`**, not the visible page
length. On home, `entry_point` is taken from route params only—no
fallback to `predict_feed`. Unit and component-view tests cover property
mapping and end-to-end firing.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
353b4bb. 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 Opus 4.8 <noreply@anthropic.com>
…money home page (#31463)

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

change perps/predict page headers when user is navigating from money
home page

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

## **Related issues**

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

Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1516

## **Manual testing steps**
NA

## **Screenshots/Recordings**
<img width="396" height="845" alt="Screenshot 2026-06-10 at 3 01 38 PM"
src="https://github.com/user-attachments/assets/27a48ebc-4eb0-49bf-a0fb-1e4aca09d8fc"
/>
<img width="396" height="849" alt="Screenshot 2026-06-10 at 3 01 50 PM"
src="https://github.com/user-attachments/assets/582e3755-b4de-4e0d-a61e-2e657e0864cc"
/>

## **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**
> Navbar copy only, driven by existing navigation params; no payment or
transaction logic changes.
> 
> **Overview**
> **Perps and Predict deposit confirmation screens** now pick the navbar
title from route params: when `payWithOption` is `MoneyAccount` (money
home entry), the header shows **Transfer to Perps** or **Transfer to
Predictions** instead of the usual deposit titles.
> 
> New English strings support those labels. **Unit tests** cover default
vs money-account titles plus existing behavior (token registration,
`CustomAmountInfo`, money-account payment override).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8917ead. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…epage (PRED-906) (#31226)

## **Description**

Integrates the reusable PRED-835 `PredictPortfolioModule` into the
redesigned Predict homepage (`PredictHome`), replacing the temporary
mock placeholder that previously sat at the top of the feed.

**Why:** PRED-906 (under the PRED-834 IA & Navigation overhaul) requires
the redesigned homepage to render the real portfolio module — value,
P&L, available balance, and the Positions / Add funds / Withdraw / Claim
actions — at the top of the single-scroll feed.

**What changed:**
- **Relocated** the module from `components/PredictPortfolio` into
`views/PredictHome/components/PredictPortfolio`, since the redesigned
homepage is its only consumer (pure `git mv` + import-depth fixes, no
behavior change).
- **Wired** the real `PredictPortfolioModule` into `PredictHome`,
removing the mock placeholder component and its unused
`portfolio_module_placeholder` string.
- Gating stays entirely at the **route level** via
`predictHomeRedesign.enabled` — the redesigned home always shows the
module, while the legacy `PredictFeed` (balance card) renders when the
redesign flag is off. No in-view flag or duplicate "Predictions" title.
- **Fixed** the portfolio Positions button, which still used an old
temporary fallback (navigating to `PREDICT.MARKET_LIST`); it now routes
to `Routes.PREDICT.POSITIONS`, matching the working `PredictBalance`
button.
- Minor spacing fix: title section bottom padding `pb-4` → `pb-2` to
match Figma.

## **Changelog**

<!-- Redesigned Predict homepage is gated behind the unreleased
`predictHomeRedesign` flag. -->

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-906

## **Manual testing steps**

```gherkin
Feature: Predict homepage portfolio module

  Scenario: user views the portfolio module on the redesigned Predict homepage
    Given the predictHomeRedesign feature flag is enabled
    When the user opens the Predict homepage
    Then the portfolio module renders at the top of the feed
    And it shows portfolio value, P&L line, available balance, and the action buttons
    And no duplicate "Predictions" title is shown

  Scenario: user opens the Positions screen from the portfolio
    Given the portfolio module is visible on the Predict homepage
    When the user taps the "Positions" button
    Then the Predict Positions screen opens

  Scenario: portfolio actions behave as in the existing feed
    When the user taps Add funds, Withdraw, or Claim
    Then the action behaves the same as the PRED-835 current-feed integration

  Scenario: fallback when the redesign flag is off
    Given the predictHomeRedesign feature flag is disabled
    When the user opens the Predict tab
    Then the legacy PredictFeed renders with the existing balance card
```

## **Screenshots/Recordings**

### **Before**

<!-- Mock "Portfolio module" placeholder box at the top of the
redesigned homepage. -->

### **After**

<img width="429" height="889" alt="Screenshot 2026-06-08 at 15 20 44"
src="https://github.com/user-attachments/assets/06f54e0c-c01b-40e1-91da-e6b42b41a8cf"
/>

<!-- Redesigned homepage now renders the real portfolio module (value,
-$ P&L, available balance, Positions/Add funds/Withdraw, Claim CTA).
Screenshot to be attached by author. -->

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI and navigation changes behind the predict home redesign path;
Positions is no longer redirected when the portfolio flag is off, which
slightly broadens access to that screen.
> 
> **Overview**
> Replaces the redesigned **Predict home** placeholder portfolio block
with the real **`PredictPortfolioModule`** (value, P&L, actions) and
hooks **deposit-wallet withdraw** to
**`PredictWithdrawUnavailableSheet`** on the home shell.
> 
> **Positions** from the module now navigates to
**`Routes.PREDICT.POSITIONS`** instead of the temporary market-list
fallback. The stack **drops the portfolio-flag gate** on the Positions
route (`PredictPositionsRoute` removed); **`PredictPositionsView`** is
registered directly, so deep links to Positions work even when the
portfolio flag is off.
> 
> Removes the mock **`PredictPortfolioModule`** placeholder folder and
the unused **`portfolio_module_placeholder`** copy; module **`testID`**
aligns with **`PredictHomeSelectorsIDs.PORTFOLIO_MODULE`**. Tests follow
the new navigation and routing behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6860016. 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 Opus 4.8 <noreply@anthropic.com>
## **Description**

Bump @metamask/eip-5792-middleware to ^3.0.4

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/40465?quickstart=1)

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

Please ping with any questions

## **Related issues**

Fixes:
https://consensys.slack.com/archives/C0318JSUBM3/p1780930822763839

## **Manual testing steps**

No user facing changes

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

- [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**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 which accounts EIP-5792 batch send and capability RPCs can use
for a given dapp origin; behavior shifts from all accounts to
permission-scoped accounts, which can break or fix dapps depending on
prior reliance on the old behavior.
> 
> **Overview**
> Upgrades **`@metamask/eip-5792-middleware`** from **^2.0.0** to
**^3.0.4** and adapts the extension/mobile wiring to the package’s new
hooks.
> 
> **EIP-5792 account scoping:** `wallet_getCapabilities` and
`wallet_sendCalls` no longer use a **`getAccounts`** callback that
returned every address from **`AccountsController`**. They now supply
**`getPermittedAccountsForOrigin`**, resolved via
**`getPermittedAccounts(request.origin)`** in `createEip5792Middleware`
and **`getPermittedAccounts(this.channelIdOrOrigin)`** on the
**`processSendCalls`** options in **`BackgroundBridge`**. Capability
queries and batched sends are tied to **origin-permitted** accounts
instead of the full account list.
> 
> **Small constant fix:** **`INTERNAL_ORIGINS`** is built with
**`.filter(Boolean)`** so a missing **`MM_FOX_CODE`** env value does not
leave a falsy entry in the array.
> 
> Tests drop the obsolete **`getAccounts`** mock from the middleware
factory smoke test.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a761a07. 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: Alex Donesky <adonesky@gmail.com>
…mentType types (#31468)

## **Description**

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

Wires the dual-framework model through the E2E test framework layer so
that Detox and Appium can share page objects without per-screen
`encapsulated()` configs.

**Matchers** — `getElementByID`, `getElementByText`, and
`getElementByLabel` now return `EncapsulatedElementType` and route
string selectors through `resolve()` from `Selector.ts`, which builds
Detox + Playwright locator pairs at runtime. RegExp inputs still go
directly to `element(by.*)` for Detox-only callers.

**Gestures, Assertions, Utilities** — parameter types updated from
`DetoxElement` to `EncapsulatedElementType`. Internal Detox-specific
paths (`waitFor`, `getAttributes`) cast with `asDetoxElement` / `await`
where needed. The redundant `DetoxElement | EncapsulatedElementType`
union is collapsed to `EncapsulatedElementType` throughout.

**Page objects** (~120 files) — getter return type annotations updated
from `DetoxElement` to `EncapsulatedElementType`. Getter bodies are
untouched. A small number of call sites that feed elements into raw
Detox APIs retain explicit casts after `await`.

No production app code is changed. All changes are confined to `tests/`.

## **Changelog**

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

CHANGELOG entry: null

## **Related issues**

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

Refs: https://consensyssoftware.atlassian.net/browse/MMQA-1927
Refs: https://consensyssoftware.atlassian.net/browse/MMQA-1928

## **Manual testing steps**

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

```gherkin
Feature: Dual-framework E2E selector layer

  Scenario: Existing Detox smoke tests continue to pass
    Given the branch is checked out
    When yarn jest tests/framework/ is run
    Then all framework unit tests pass with no regressions

  Scenario: TypeScript compilation is clean
    Given the branch is checked out
    When yarn lint:tsc is run
    Then zero TypeScript errors are reported
```

## **Screenshots/Recordings**

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

N/A — test infrastructure only, no UI changes.

### **Before**

N/A

### **After**

N/A

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Broad typing and matcher behavior changes across the entire E2E stack
could break Detox runs if resolve() or await/cast paths are wrong,
though scope is limited to tests/.
> 
> **Overview**
> Standardizes the E2E layer on **`EncapsulatedElementType`** so Detox
and Appium can share page objects without rewriting every screen.
> 
> **Matchers** — `getElementByID`, `getElementByText`, and
`getElementByLabel` are now synchronous and return
`EncapsulatedElementType`. String selectors go through **`resolve()`**
in `Selector.ts`, which pairs Detox locators with Playwright/Appium
equivalents; **RegExp** matchers still call Detox `element(by.*)`
directly.
> 
> **Gestures, Assertions, Utilities** — APIs accept
`EncapsulatedElementType` instead of `DetoxElement`. Text-based
assertions and legacy helpers use **`asDetoxElement`** (or `await` +
casts) before `waitFor` / `getAttributes`.
> 
> **Page objects** — Getter return types move from `DetoxElement` to
`EncapsulatedElementType` across the suite; a few spots that call raw
Detox APIs keep explicit casts after `await`.
> 
> **EncapsulatedElement** — `LocatorConfig.detox` factories are typed to
return `EncapsulatedElementType`, with internal casts when materializing
Detox elements.
> 
> All changes are under `tests/`; no production app code.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0100ae2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…1462)

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

QuickBuy surfaces its own pending/complete/failed toasts. When a trade
is submitted via an EIP-7702 smart account or a gas-included
(`gasIncluded7702`) quote, `BridgeStatusController.submitTx` wraps the
trade as a `TransactionType.batch` transaction. `batch` is in
`REDESIGNED_TRANSACTION_TYPES`, so `NotificationManager` also fired the
generic "Transaction submitted" and "Transaction #N complete" toasts on
top of QuickBuy's own — the user saw **4 toasts instead of 2**. (This is
why it doesn't reproduce on a plain EOA: those trades are typed
`swap`/`bridge`, which are not redesigned types.)

This PR scopes the suppression strictly to QuickBuy via inversion of
control, so the main Swap/Bridge flows are unaffected:

- New dependency-free module `app/core/notificationSkipPredicates.ts`
holds a registry of "skip predicates".
`NotificationManager.#shouldSkipNotification` consults it (guarded by
try/catch so a throwing predicate can never break notifications).
Keeping it in its own module means feature code can register without
importing the heavy `NotificationManager` graph.
- QuickBuy registers a predicate at the app root
(`useQuickBuyToastRegistrations`) that matches:
- **tracked trade ids** — covers the terminal complete/failed toast, and
- **in-flight submissions** via a pre-submit marker
(`beginQuickBuySubmission`/`endQuickBuySubmission`) — covers the
*pending* toast, which fires mid-`submitTx` before the tx id is known.

## **Changelog**

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

CHANGELOG entry: Fixed QuickBuy showing duplicate transaction toasts for
smart-account and gas-included trades

## **Related issues**

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

Fixes: https://consensyssoftware.atlassian.net/browse/TSA-627

## **Manual testing steps**

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

```gherkin
Feature: QuickBuy transaction toasts

  Scenario: User runs a QuickBuy with a smart (EIP-7702) account
    Given I have an EIP-7702 smart account enabled on the trade network
    And I open a token from the Social Leaderboard trader position view
    When I confirm a QuickBuy trade
    Then I see exactly one QuickBuy "Buying..." pending toast
    And after the trade settles I see exactly one QuickBuy "Bought" (or "Failed") toast
    And I do not see the generic "Transaction submitted" or "Transaction complete" toasts

  Scenario: User runs a QuickBuy on a plain EOA (regression)
    Given I have a standard externally-owned account
    When I confirm a QuickBuy trade
    Then I still see only the two QuickBuy toasts (pending, then terminal)

  Scenario: Main Swap/Bridge flows are unaffected (regression)
    Given I start a swap or bridge from the main Swap/Bridge UI
    When the transaction is submitted and confirmed
    Then the generic transaction toasts still appear as before
```

## **Screenshots/Recordings**

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

### **Before**

In some rare cases, this happens:


https://github.com/user-attachments/assets/838826be-99a7-4101-873e-88173059fbc1

### **After**

This is what we want in all cases:


https://github.com/user-attachments/assets/b49de0d5-a0e2-47cb-8eef-6b372ad0f79f



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

Made with [Cursor](https://cursor.com)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes global notification gating and briefly suppresses
swap/bridge/batch notifications during QuickBuy submit; predicates are
isolated and errors are caught so unrelated flows should still get
generic toasts.
> 
> **Overview**
> Adds a **dependency-free notification skip registry**
(`notificationSkipPredicates`) and wires **`NotificationManager`** to
consult registered predicates (with try/catch) so feature flows can opt
out of generic transaction toasts without core importing UI code.
> 
> **QuickBuy** registers `isQuickBuyTransaction` at the app root and
extends the trade tracker with: a **ref-counted in-flight submission
marker** (`begin`/`end` around `submitTx`) for pending notifications
before the tx id exists; **tracked and recently-settled tx ids** so
suppression still applies when `NotificationManager` re-checks ~2s after
confirm/fail; and **type-narrowed matching** (swap/bridge/batch) during
submission so unrelated txs are not hidden. Terminal handling now
**settles** trades via `markQuickBuyTradeSettled` instead of a plain
untrack so duplicate generic success/error toasts do not appear on top
of QuickBuy’s own toasts (notably EIP-7702 `batch` trades).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
270b0ec. 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 b3e7eb8 into Reality2byte:main Jun 10, 2026
1 of 12 checks passed
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.