Skip to content

[pull] main from MetaMask:main#787

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

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

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 26, 2026

See Commits and Changes for more details.


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

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

caieu and others added 12 commits May 26, 2026 18:29
## **Description**

Adds remote-config support for the new Predict portfolio feature flag
introduced by PRED-898.

This PR wires `predictPortfolio` through the existing Predict feature
flag pipeline:

- Adds the typed flag shape: `{ enabled: boolean; minimumVersion: string
}`
- Defaults malformed or missing config to `{ enabled: false,
minimumVersion: '' }`
- Parses the flag through the Predict schema/resolver flow
- Applies existing version-gated feature flag validation
- Exposes `selectPredictPortfolioEnabledFlag`
- Covers default, enabled, disabled, malformed, progressive rollout, and
minimum-version behavior in unit tests

This PR does not gate UI or routes yet. It only adds the flag
infrastructure needed for the portfolio module and positions screen to
consume later.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:
[PRED-898](https://consensyssoftware.atlassian.net/browse/PRED-898)

## **Manual testing steps**

```gherkin
Feature: Predict portfolio remote feature flag

  Scenario: portfolio flag is absent or disabled
    Given the Predict remote config does not include predictPortfolio
    When the Predict feature flags are resolved
    Then selectPredictPortfolioEnabledFlag returns false

  Scenario: portfolio flag is enabled for the current app version
    Given the Predict remote config includes predictPortfolio enabled with a passing minimumVersion
    When the Predict feature flags are resolved
    Then selectPredictPortfolioEnabledFlag returns true

  Scenario: portfolio flag config is malformed
    Given the Predict remote config includes malformed predictPortfolio values
    When the Predict feature flags are resolved
    Then selectPredictPortfolioEnabledFlag returns false
```

## **Screenshots/Recordings**

N/A. This is feature flag plumbing only and has no user-visible UI
changes.

### **Before**

N/A

### **After**

N/A

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

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

## Test plan

```bash
node .yarn/releases/yarn-4.14.1.cjs jest app/components/UI/Predict/schemas/flags.test.ts app/components/UI/Predict/utils/resolvePredictFeatureFlags.test.ts app/components/UI/Predict/selectors/featureFlags/index.test.ts
```

Result: 3 test suites passed, 139 tests passed.


[PRED-898]:
https://consensyssoftware.atlassian.net/browse/PRED-898?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Flag-only plumbing with conservative false defaults; no user-facing
routes or trading behavior changes in this diff.
> 
> **Overview**
> Adds **Predict portfolio** remote feature flag plumbing so later work
can gate portfolio UI and routes without changing this PR.
> 
> The remote key `predictPortfolio` is resolved through the existing
version-gated boolean path in `resolvePredictFeatureFlags`, surfaced on
`PredictFeatureFlags` as `predictPortfolioEnabled`, and exposed via
`selectPredictPortfolioEnabledFlag`. Defaults stay **off** when the flag
is missing, invalid, below `minimumVersion`, or not version-gated
(including empty `minimumVersion`). Unit tests cover resolver and
selector behavior (enabled/disabled, malformed config, progressive
rollout unwrap).
> 
> **No navigation or UI is gated yet**—only flag infrastructure and
tests.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8081140. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…on, and Spending Limit (#30571)

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

Polishes the Money Account integration across the Card surface so that
money-account funding sources are identified, labeled, and managed
consistently from Card Home through Spending Limit.

What changed:

- **`CardFundingToken.isMoneyAccountEntry` flag** baked at the selector
layer (`selectCardPrimaryToken`, `selectCardFundingTokens`,
`selectCardAvailableTokens`, `selectCardLineaUsdcToken`) via a pure
`isMoneyAccountEntry(walletAddress, moneyAccounts)` predicate. All
consumers now read one boolean instead of recomputing the comparison;
future Veda-vault detection becomes a one-line change in the predicate.
- **Card Home** — SVG hex address on the card art is replaced with the
localized `"Money account"` label when
`primaryToken.isMoneyAccountEntry` is true; `truncateAddress` no longer
mangles non-hex input.
- **Add Funds** — when the primary funding source is a money account,
the button stays enabled and routes to the existing `MoneyAddMoneySheet`
(`Routes.MONEY.MODALS.ADD_MONEY_SHEET`) instead of going through swap /
deposit checks.
- **Asset Selection sheet** — per-row hex address is substituted with
the money-account label whenever the row's token is a money-account
entry (covers both the primary-source case and other rows).
- **Spending Limit** introduces a fourth `flow` value `'enable_card'`
(Card Home "Enable Card" CTA) so the screen can behave like onboarding
for token + Money-Account logic without triggering onboarding-only
chrome (hidden back button, replace-style routing). Entry-point flow
labels were also corrected: `manageSpendingLimitAction` → `'manage'`,
the `AssetSelectionBottomSheet` NotEnabled tap → `'enable'`.
- **Spending Limit lock semantics** — new `isMoneyAccountLocked` boolean
decouples the locked state from `isMoneyAccountSource`. Lock fires only
on the manage flow when `priorityToken.isMoneyAccountEntry` is true
(regardless of balance). Onboarding-like flows still preselect Money
Account when funded but rows remain pressable; switching the account via
the picker now surfaces a Spend-and-Earn CTA.
- **Money Account delegation re-submit** — `useMoneyAccountCardLinkage`
exposes a new `canSubmitMoneyAccountDelegation` predicate that does not
include `!isAlreadyDelegated`. `confirmLinkInBackground` now uses it, so
Manage Limit can update the cap (or revoke by submitting `'0'`) on an
already-delegated money-account token. `canLink` continues to gate the
first-time link CTA.
- **Render-tree cleanup** — Account and Token rows on the Spending Limit
screen are now dedicated subcomponents (`AccountRow`, `TokenRow` under
`Views/SpendingLimit/components/`), removing ~180 lines of inline IIFE
JSX from the parent and stripping 12 unused imports. Chevron is shown
when the row is actionable: account row when not locked; token row only
when not locked AND not money-account-sourced.

Reason for the change: Money Account is being introduced as an EOA-style
funding source for the Card. Without these polishes, the user would see
raw hex addresses, be unable to update the delegation cap after the
initial link, and get inconsistent flow gating between Card Home entry
points.

## **Changelog**

CHANGELOG entry: Improved the Card experience for Money Account users —
Card Home and Asset Selection now display "Money account" instead of the
raw address, Add Funds routes to the Money Account add-funds sheet, and
Manage Limit can update or revoke the delegation cap on an
already-linked Money Account.

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Money Account on Card

  Scenario: Card Home renders the Money Account label
    Given the user has a Money Account as primary funding source
    When they open Card Home
    Then the card art shows "Money account" instead of the raw hex address
    And no raw hex address appears in any Card Home surface

  Scenario: Add Funds routes to the Money Account sheet
    Given the user has a Money Account as primary funding source
    When they tap "Add funds" on Card Home
    Then the Money Account add-funds bottom sheet opens
    And the standard swap/deposit flow is not triggered

  Scenario: Asset Selection labels money-account rows
    Given the user has at least one money-account token in their funding list
    When they open the Asset Selection bottom sheet
    Then each money-account row shows "Money account" in place of the hex address

  Scenario: Manage Limit locks rows when the primary token is a Money Account
    Given the user has a Money Account as primary funding source
    When they tap "Manage limit"
    Then both the Account and Token rows are locked (non-pressable, no chevron)
    And the Spend-and-Earn CTA is not shown

  Scenario: Manage Limit updates the delegation cap on an already-delegated Money Account
    Given the user has previously linked their Money Account to the Card
    When they open Manage Limit, change the limit (or set it to 0 to revoke), and confirm
    Then the controller re-runs linkMoneyAccountCard with the new delegation amount
    And the success toast is shown

  Scenario: Enable Card from Card Home shows the Money Account CTA
    Given the user has funds in their Money Account
    And they have a Card but no delegation yet
    When they tap "Enable Card" on Card Home
    Then Money Account is preselected as the source
    And the back button is visible
    And switching to another account via the picker reveals the "Spend & Earn" CTA

  Scenario: Enabling a NotEnabled token from Asset Selection shows the Money Account CTA
    Given the user has funds in their Money Account
    When they tap a NotEnabled token in the Asset Selection sheet
    Then Spending Limit opens with that token preselected
    And the "Spend & Earn" CTA is visible
    And tapping it switches the source to Money Account
```

## **Screenshots/Recordings**

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

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

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

## **Pre-merge reviewer checklist**

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

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes card funding navigation, delegation submit paths, and
spending-limit flow gating across several user-facing entry points;
well-covered by tests but touches money-account linkage behavior.
> 
> **Overview**
> This PR unifies **Money Account** as a Card funding source end-to-end:
selectors set **`isMoneyAccountEntry`** on funding tokens (via
`isMoneyAccountEntry(walletAddress, moneyAccounts)`), and Card surfaces
use that flag instead of re-deriving wallet matches.
> 
> **Card Home & funding actions:** The card art shows the localized
**Money account** label instead of hex when the primary token is a
money-account entry; **`truncateAddress`** leaves non-hex labels intact.
**Add funds** stays enabled without swaps and opens
**`MoneyAddMoneySheet`**. **Enable card** navigates to Spending Limit
with **`flow: 'enable_card'`**; **Manage spending limit** uses **`flow:
'manage'`** (tests/views aligned).
> 
> **Spending Limit:** New **`enable_card`** flow separates “enable card
from home” from onboarding/manage. **`isMoneyAccountLocked`** locks
account/token rows only on **manage** when the priority token is a
money-account entry; onboarding-like flows keep rows pressable and
adjust Money Account CTA / balance-loading rules. Account/token UI moves
into **`AccountRow`** / **`TokenRow`**.
> 
> **Delegation:** **`canSubmitMoneyAccountDelegation`** allows
**`confirmLinkInBackground`** when already delegated so users can update
cap or revoke (`'0'`).
> 
> **Asset selection:** NotEnabled tokens open Spending Limit with
**`flow: 'enable'`**; rows show the money-account label instead of
truncated hex when applicable.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9e2c14f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Removes the unused temporary CustomSpendCap component from
`app/component-library/components-temp/CustomSpendCap`.

This also removes its nested CustomInput helper, which was only used by
CustomSpendCap. A reference search confirms there are no remaining app,
test, or Storybook references.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: temporary component cleanup

  Scenario: CustomSpendCap temp component is removed
    Given the repository is on this branch
    When searching for CustomSpendCap and CustomInput references
    Then no app, test, or Storybook references are found
```

## **Screenshots/Recordings**

N/A. Code cleanup only.

### **Before**

N/A

### **After**

N/A

## **Validation**

- `rg -n
"CustomSpendCap|CustomInput|CUSTOM_SPEND_CAP|CUSTOM_INPUT|components-temp/CustomSpendCap"
app tests .storybook --glob '!node_modules/**'` returned no matches.

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk cleanup that removes an unused temporary component and its
tests/styles; functional behavior should be unchanged as there are no
remaining references.
> 
> **Overview**
> Removes the unused temporary `CustomSpendCap` component under
`app/component-library/components-temp/CustomSpendCap`, including its
nested `CustomInput` helper, constants, styles, types, index exports,
and associated tests.
> 
> Updates `docs/bigint-migration-guide.md` to drop the now-removed
`CustomSpendCap` entries from the team ownership/burndown list.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
11caeab. 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 Agent <cursoragent@cursor.com>
Co-authored-by: George Marshall <georgewrmarshall@users.noreply.github.com>
Co-authored-by: George Marshall <george.marshall@consensys.net>
## **Description**

On the Tokens full view, opening the Sort bottom sheet silently reset
the user's sort preference back to "Declining balance" — even when no
row was tapped. The reset originated from a `useFocusEffect` cleanup,
which fires on **blur** (e.g. when a modal opens on top) rather than
only on unmount.

Switched to `useEffect` with an unmount-only cleanup so the reset still
fires when the user leaves the full view back to the wallet, but does
not fire when the screen merely blurs behind the sort sheet.

## **Changelog**

CHANGELOG entry: Fixed a bug where the Tokens full view sort preference
would silently reset to "Declining balance" when opening the Sort bottom
sheet.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-813

## **Manual testing steps**

```gherkin
Feature: Token sort preference persists across opening the Sort bottom sheet

  Scenario: User selects Alphabetical, then re-opens the Sort sheet
    Given the user is on the Tokens full view
    And tokens are currently sorted by "Declining balance ($ high-low)"

    When the user taps the sort icon
    And the user taps "Alphabetically (A–Z)"
    Then the sheet dismisses
    And tokens are sorted alphabetically A–Z

    When the user taps the sort icon again
    Then the Sort bottom sheet shows "Alphabetically (A–Z)" as selected
    And tokens behind the sheet remain in alphabetical order

    When the user dismisses the sheet without tapping any row
    Then tokens remain in alphabetical order

  Scenario: Sort resets when leaving the full view
    Given the user is on the Tokens full view with sort set to "Alphabetically"

    When the user taps back (or swipes back) to the wallet
    And re-enters the Tokens full view
    Then the sort is reset to the default "Declining balance"
```

## **Screenshots/Recordings**


https://github.com/user-attachments/assets/6afc8d00-0626-41bc-9c7a-b3d749e93177

### **Before**

Uploading before-sort.mov…

### **After**


https://github.com/user-attachments/assets/6afc8d00-0626-41bc-9c7a-b3d749e93177

## **Pre-merge author checklist**

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

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

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

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

- [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**
> Small lifecycle fix in one screen; behavior is more correct and
limited to token sort preferences when a feature flag is on.
> 
> **Overview**
> Fixes token sort resetting when the Sort bottom sheet opens on
**Tokens full view**.
> 
> **`TokensFullView`** previously used **`useFocusEffect`** cleanup to
restore **`DEFAULT_TOKEN_SORT_CONFIG`** when homepage sections v1 is
enabled. That cleanup runs on **blur** (e.g. modal/sheet on top), so
opening the sort sheet wiped the user's choice without a tap.
> 
> The reset now runs only on **unmount** via **`useEffect`** cleanup, so
sort persists while the sheet is open and still resets when leaving the
full view back to the wallet.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2051fd5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Fixes the Activity tab underline getting stuck on the wrong tab after
changing the network filter, when conditional tabs (e.g. Perps) are
added or removed.

<details>
<summary>Detailed explanation (root cause and fix)</summary>

### Root cause
The underline position is driven by measurements captured in
`tabLayouts.current` and updated by `onLayout` callbacks in `TabsBar`.

When the set of tabs changes (for example, Perps becomes
enabled/disabled after a network filter change), `TabsBar` was resetting
`tabLayouts.current` with `new Array(tabs.length)`. This produces a
**sparse array**.

`TabsBar` then used `Array.prototype.every(...)` to determine that all
tab layouts were available. On sparse arrays, `every(...)` **skips
holes**, so the check could incorrectly return `true` even when only a
subset of tab layouts had actually been measured.

That caused `layoutsReady` to become `true` prematurely and allowed
`animateToTab(...)` to run against incomplete layout data. In the common
failure case, the underline would animate to the last-measured tab and
then refuse to move when the user switched back to a tab whose layout
entry was still missing, leaving the underline stuck on the wrong tab.

### Fix
This PR makes the layout readiness checks robust and ensures we
re-measure after structural tab changes:

1. **Use dense arrays for layout resets**
- Replace `new Array(n)` with `Array.from({ length: n }, () =>
undefined)` so the array has no holes.

2. **Make "all measured" checks strict**
- Introduce `areAllTabLayoutsMeasured(...)`, which requires
`layouts.length === tabCount` and that every slot is defined with a
positive width.

3. **Force re-measurement when tabs change**
- Add a `layoutGeneration` value that is incremented on structural tab
changes and included in the `Tab` key so React remounts each `Tab` and
reliably re-fires `onLayout` after add/remove.

4. **Recover underline position as soon as the active tab is measured**
- After storing a layout, if the active tab has a valid layout we
schedule `animateToTab(activeIdx)` via `requestAnimationFrame`, so the
underline updates promptly even while other tabs are still measuring.

### What changed in practice
- After a tab add/remove, the underline will only be considered ready
once measurements exist for the relevant tab(s).
- The active tab's underline can re-position as soon as that tab's
layout is available, preventing the underline from vanishing or sticking
during transitions.

</details>

## **Changelog**

CHANGELOG entry: Fixed a bug where the Activity tab underline could get
stuck on the wrong tab after changing networks.

## **Related issues**

Fixes: #30365

Relates-to: https://consensyssoftware.atlassian.net/browse/TMCU-755

## **Manual testing steps**

```gherkin
Feature: Activity tab underline stays in sync when tabs change

  Scenario: underline remains aligned after network filter changes tab set
    Given the user is on the Activity screen with multiple tabs visible (e.g. Transactions, Transfers, Perps, Predictions)
    And the tab underline is visible under the active tab

    When the user changes the network filter so a conditional tab is removed (e.g. Perps)
    Then the underline stays aligned with the currently selected tab

    When the user changes the network filter so the conditional tab is added back
    And the user switches between Transactions, Transfers, Perps, and Predictions
    Then the underline follows the active tab and does not get stuck on a different tab
```

## **Screenshots/Recordings**

### **Before**

Tab underline could remain under the wrong tab (e.g. Predictions) while
Transactions content was shown, after changing the network filter.

### **After**

Tab underline stays aligned with the active tab when conditional tabs
are added or removed.


https://github.com/user-attachments/assets/5f320905-6daa-451d-8db8-379ff7621a28


## **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 — existing `TabsBar` unit tests
pass (55/55); no new tests required for layout measurement edge case
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable — N/A (inline helpers are self-explanatory)
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Localized TabsBar layout/animation logic with no auth, data, or API
impact; behavior change is limited to tab underline positioning and
scroll detection timing.
> 
> **Overview**
> Fixes the **TabsBar** animated underline staying on the wrong tab when
the tab list changes (e.g. conditional Activity tabs appearing after a
network filter change).
> 
> Layout state now uses **dense** arrays (`Array.from` + `undefined`
slots) and a strict **`areAllTabLayoutsMeasured`** helper so
sparse-array holes cannot make readiness checks pass early. On
structural tab changes, **`layoutGeneration`** is bumped and included in
each **`Tab`** `key` to remount tabs and re-fire **`onLayout`**,
replacing the old container-width reset trick.
> 
> **`handleTabLayout`** repositions the underline via
**`requestAnimationFrame`** as soon as the **active** tab has valid
measurements, and **`activeIndex`** animation only runs when all layouts
are truly measured.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cb4c130. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Replaces the bar chart stepper asset (`mm_how_it_works.png`) used in the
"How it works" section on the Money home with the updated illustration
from the design spec.

Reference: [Figma — Update the how it works image on
home](https://www.figma.com/design/XKZ8hRqSn2iTiuzmlQLuYQ/Money-account?node-id=8391-20692&m=dev)

## **Changelog**

CHANGELOG entry: null

## **Related issues**

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

## **Manual testing steps**

```gherkin
Feature: Updated "How it works" illustration on Money home

  Scenario: user opens the Money home
    Given the user has the Money account feature enabled
    When the user navigates to the Money home screen
    Then the "How it works" section displays the updated bar chart stepper illustration
```

## **Screenshots/Recordings**

### **Before**

### **After**

<img width="1206" height="2622" alt="image"
src="https://github.com/user-attachments/assets/d913cf78-0705-49d0-9515-ba4916935480"
/>


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

## **Pre-merge reviewer checklist**

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

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Visual-only PNG replacement with no logic, navigation, or API changes.
> 
> **Overview**
> Swaps the **`mm_how_it_works.png`** illustration for the design-spec
bar chart stepper. **No app code or import paths change**—existing
references in **`MoneyCondensedInfoCards`** (and its tests) keep the
same filename, so the **How it works** condensed card on Money home
shows the new artwork automatically.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b37bfcb. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Migrates the MultichainAccounts temporary component internals away from
deprecated component-library imports and onto
`@metamask/design-system-react-native` equivalents.

This updates account rows, external account rows, account selector list
pieces, address rows, checkboxes, search fields, text, icons, sensitive
text, and avatar usage. It also adds a small avatar variant
compatibility helper so legacy persisted selector values such as
`Maskicon`, `JazzIcon`, and `Blockies` continue to map to the DS
`AvatarAccountVariant` API.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/MUL-1693

## **Manual testing steps**

```gherkin
Feature: Multichain account component DS migration

  Scenario: account selector and address rows preserve existing behavior
    Given a wallet with multichain account groups and network addresses

    When the account selector and multichain address list are opened
    Then account rows, external account rows, network avatars, checkboxes, copy actions, and search fields render and behave as before
```

## **Screenshots/Recordings**


https://github.com/user-attachments/assets/153a7587-0b2f-4398-b894-d768577452dc

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it replaces core account-selector UI primitives
(avatars, text, search, checkbox, toast wiring) with Design System
components, which could cause subtle rendering/interaction regressions
across account selection flows.
> 
> **Overview**
> Migrates the temporary MultichainAccounts UI components
(`AccountCell`, selector list cells/headers, external account rows,
address rows, and address rows list) from deprecated component-library
primitives to `@metamask/design-system-react-native` equivalents (text,
icons, sensitive text, avatars, search, checkbox).
> 
> Introduces `avatarAccountVariant` compatibility helpers to map legacy
persisted avatar type strings (`Maskicon`/`JazzIcon`/`Blockies`) to DS
`AvatarAccountVariant`, and updates tests to assert DS
components/testIDs (including new checkbox icon and network avatar test
IDs) and to use timer-safe `act`/fake-timer handling for copy/toast
feedback.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9a035d1. 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.
-->

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

Part **2 of 2** for
[MUL-1689](https://consensyssoftware.atlassian.net/browse/MUL-1689).
**Base branch:** `gar/refactor/mul-1689/part-1` (merge part 1 first, or
rebase this branch onto `main` after part 1 lands and retarget to
`main`).

Migrates the Account Details **shell** and related tests from
`app/component-library` to `@metamask/design-system-react-native`:

- `BaseAccountDetails/index.tsx` — design-system `HeaderBase`
(`titleTestID` for title queries), `startButtonIconProps` for back,
`AvatarAccount` with mapping from settings `AvatarAccountType` to
`AvatarAccountVariant`, `Text` / `Icon` with semantic tokens.
- `BaseAccountDetails/styles.ts` — remove obsolete text color style now
covered by `TextColor`.
- `SmartAccountModal.tsx` — design-system `HeaderBase`, `Text`, nested
learn-more `Text` with `TextColor.InfoDefault`.
- Tests — narrower `AvatarAccountType` import path;
`HardwareAccountDetails` header title assertion uses the same
`header-title` test id as design-system `HeaderBase`.

Depends on part 1 for a consistent Account Details tree.

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

Refs: https://consensyssoftware.atlassian.net/browse/MUL-1689

## **Manual testing steps**

```gherkin
Feature: Multichain Account Details (shell and modal, part 2)

  Scenario: Account details header and back navigation
    Given the user opened Multichain Account Details from the accounts flow
    When the header shows the account name
    Then the back control returns to the previous screen without layout regressions

  Scenario: Smart Account modal from account details
    Given an EVM account with Smart Account entry available
    When the user opens the Smart Account modal
    Then the modal title and description render and Learn more opens the webview flow as before
```

## **Screenshots/Recordings**
<img width="300" alt="Simulator Screenshot - iPhone 16 - 2026-05-19 at
17 19 07"
src="https://github.com/user-attachments/assets/fb332055-a10a-4d22-abf4-661c26499c11"
/> <img width="300" alt="Simulator Screenshot - iPhone 16 - 2026-05-19
at 17 20 09"
src="https://github.com/user-attachments/assets/592473e8-871f-48c6-ab95-a18c52276257"
/>


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


[MUL-1689]:
https://consensyssoftware.atlassian.net/browse/MUL-1689?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only component swap with preserved navigation, test IDs, and avatar
mapping; no auth or credential logic changes in this diff.
> 
> **Overview**
> Migrates the multichain **Account Details** shell
(`BaseAccountDetails`) and **Smart Account** modal from
`app/component-library` to `@metamask/design-system-react-native`,
keeping behavior and test IDs aligned with the previous UI.
> 
> **`BaseAccountDetails`** now uses design-system `HeaderBase` with
`titleTestID` `header-title` and `startButtonIconProps` for back
(replacing `ButtonLink` + `Icon`), `AvatarAccount` with a
`toAvatarAccountVariant` bridge from settings `AvatarAccountType`, and
`Text` / `Icon` with `TextVariant`, `FontWeight`, `TextColor`, and
`IconColor` instead of theme-driven styles. Row styling drops the local
`text` color rule in favor of tokens.
> 
> **`SmartAccountModal`** follows the same header pattern and updates
body copy to design-system `Text`, including learn-more styling via
`TextColor.InfoDefault`.
> 
> Related unit tests import `AvatarAccountType` from the narrower path
and assert the header title via `header-title` where the legacy
`HEADERBASE_TITLE_TEST_ID` constant was removed.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
92afdd3. 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>
## **Description**

Adds address poisoning detection to the send flow recipient screen. When
a user enters a recipient address, the app now checks whether the
address is similar to a known address from the user's address book or
transaction history.

When a poisoning match is detected:
- An error banner warns the user about the potential address poisoning
attack
- An expandable accordion lets the user compare the entered address
against the known safe address, with differing characters highlighted
for easy visual identification
- The paste-to-auto-review flow is blocked so the user must manually
acknowledge and proceed

## **Changelog**

CHANGELOG entry: Added address poisoning detection warning to the send
flow, alerting users when a recipient address is suspiciously similar to
a known address

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Address poisoning detection in send flow

  Scenario: User enters an address that looks similar to a known address
    Given the user has previously transacted with address 0xABC...1234
    And a poisoning address 0xABC...5674 exists (matching prefix/suffix)

    When user enters the poisoning address as a recipient
    Then an error banner displays "Address poisoning detected"
    And the banner contains an expandable "Compare addresses" accordion
    And expanding the accordion shows both addresses with differing characters highlighted
    And the entered address shows diffs in red
    And the known address shows diffs in green

  Scenario: User pastes a poisoning address
    Given the user has previously transacted with a known address
    And a poisoning address exists for that known address

    When user pastes the poisoning address into the recipient field
    Then the auto-review flow is blocked
    And the user sees the poisoning warning before proceeding

  Scenario: User enters a safe address
    Given the user enters a valid recipient address
    And no similar known addresses are found

    When the address validation completes
    Then no poisoning warning is shown
    And the Review button is enabled as usual
```

## **Screenshots/Recordings**

### **Before**

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

### **After**
<img width="568" height="1084" alt="Screenshot 2026-03-10 at 5 07 36 PM"
src="https://github.com/user-attachments/assets/e9540d4f-c8f8-445c-86c0-3345e6e25941"
/>

<img width="568" height="1084" alt="Screenshot 2026-03-10 at 5 07 45 PM"
src="https://github.com/user-attachments/assets/c8fa3135-5ba8-49be-b7f1-a3c0d75b35ee"
/>

<img width="568" height="1084" alt="Screenshot 2026-03-10 at 11 29
35 PM"
src="https://github.com/user-attachments/assets/cb16aa73-4402-40ca-8a49-7fdebf2449c4"
/>

<img width="568" height="1084" alt="Screenshot 2026-03-10 at 11 29
52 PM"
src="https://github.com/user-attachments/assets/9b1c81c1-bfe5-4c77-81c7-33df7546fe3a"
/>


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

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches recipient selection and confirmation UX for fund transfers and
Engine controller init order; alerts are non-blocking but wrong
detection could annoy users or miss attacks.
> 
> **Overview**
> Adds **address poisoning detection** across send and transaction
confirmation using `PhishingController.checkAddressPoisoning`, backed by
`@metamask/phishing-controller` **^17.2.0** and
**address-book-controller** **^7.1.2**.
> 
> On the **send recipient** screen, a validated recipient is checked for
lookalike addresses; matches show an error banner with a **Compare
addresses** accordion (`DiffHighlightedAddress` highlights diffs in red
vs. green). **Paste-to-auto-review** is skipped when a poisoning match
exists so users must proceed manually.
> 
> **Transfer confirmation** surfaces the same risk via
`useAddressPoisoningAlert` (non-blocking danger alert with compare UI),
**From/To** row styling (error text + “Poisoned” badge), and alert
metrics. **Engine** initializes `AddressBookController` before
`PhishingController`, extends the phishing messenger for address book
state, and tests hydration of known recipients from persisted address
book.
> 
> Minor: full-screen confirm scroll content uses `flexGrow` instead of
`flex`; new i18n strings for poisoning and compare labels.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
165233f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
….79.0 (#29755)

## **Description**

Play Store–shaped Android issues (merged `prodRelease` manifest / AAB
structure) are easy to miss until late in release. This PR runs
**non-blocking** checks on the **same AAB** produced by `build.yml`
after the Android Gradle release step, instead of a separate CI job on
every PR.
**What changed:**
- After `main` Android **Release** builds (not Debug / e2e), run
`:app:lintProdRelease` and **bundletool validate** on the existing
`prodRelease` AAB via `scripts/android-play-store-check-slack.mjs`
(always exits 0; failures are collected, not job-fatal).
- For **`main-rc`** builds, upload `android-play-store-check-slack.md`
and surface failures in the **Slack RC notification**
(`slack-rc-notification.yml` + `slack-rc-notification.mjs`).
- Add a reusable composite action
(`.github/actions/android-play-store-manifest-check`) for
standalone/manual validation; add `lint-baseline.xml` and CI Gradle
tweaks so lint can run in GHA.
RC builds keep shipping; release owners get lint/bundletool issues in
Slack when checks fail.

## **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/MCRM-73 
https://consensyssoftware.atlassian.net/browse/MCWP-478

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**

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

### **Before**

<img width="737" height="575" alt="image"
src="https://github.com/user-attachments/assets/864b6672-f355-4715-bce4-6ecc5ecc5c03"
/>

### **After**

<img width="743" height="742" alt="image"
src="https://github.com/user-attachments/assets/64ef5ef3-3d97-4f52-98af-bdc27a9388ea"
/>

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

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

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

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
https://consensyssoftware.atlassian.net/browse/WAPI-1386

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

Currently when a MM Connect deeplink is used once, it cannot be used
again even if the connection failed to establish for some reason unless
the app is restarted. This PR changes it so that a deeplink can be
reused but only if a connection has not already successfully been
established with it.

## **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: Improves MM Connect deeplink handling

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**

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

### **Before**

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

### **After**

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

## **Pre-merge author checklist**

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

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

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

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

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches connection-establishment control flow and capacity eviction
ordering; mistakes could lead to skipped connections or unexpected
evictions, though changes are localized and covered by new tests.
> 
> **Overview**
> **Enables sequential reuse of MM Connect (MWP) deeplinks after
failures** by clearing the in-flight deeplink guard (`deeplinks`) in a
`finally` block, so the same URL can be retried without restarting the
app.
> 
> Adds a duplicate-connection safeguard in `handleConnectDeeplink` to
**skip creating a new connection when a connection with the same session
`id` already exists**, and updates tests to cover sequential duplicate
handling and retry-after-failure behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3e1f731. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

The APY for the `dev-monad` vault for the money account is zero, but we
don't show it, making UAT and design reviews harder

## **Changelog**

CHANGELOG entry: show even zero APY values on money home

## **Related issues**

Fixes: N/A

## **Manual testing steps**

```gherkin
Feature: my feature name

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

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

## **Screenshots/Recordings**


### **Before**

<img width="735" height="1600" alt="image"
src="https://github.com/user-attachments/assets/17df48b0-435e-4d8f-b24e-b41be37db0f2"
/>

### **After**

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

## **Pre-merge author checklist**


- [X] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [X] I've completed the PR template to the best of my ability
- [X] I've included tests if applicable
- [X] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [X] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

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

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

## **Pre-merge reviewer checklist**

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

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small Money UI visibility change with tests; no auth, payments, or
data-layer changes.
> 
> **Overview**
> Money home now **shows APY when the rate is 0%**, not only when it is
strictly positive. **`MoneyBalanceSummary`**, **`MoneyHowItWorks`**, and
**`MoneyWhatYouGet`** switch from `isPositiveNumber` to a new
**`isPositiveNumberOrZero`** guard so the label, info button, and inline
copy render for zero APY while **undefined** and **negative** values
stay hidden.
> 
> Unit tests are updated to expect visible **0% APY** UI and
**`number.test.ts`** covers the new helper.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
009b809. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators May 26, 2026
@pull pull Bot added the ⤵️ pull label May 26, 2026
@pull pull Bot merged commit bf0f9c2 into Reality2byte:main May 26, 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.