[pull] main from MetaMask:main#824
Merged
Merged
Conversation
… fees are subsidized (#31485) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section blocks the PR check. Sections without a directive are checked for structural presence only. --> ## **Description** Adds the "Paid by MetaMask" tag for Money account deposits when fees are subsidized. <!-- mms-check: type=text required=true --> <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- mms-check: type=changelog required=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: added paid by metamask tag for Money account deposits when fees are subsidized. ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [MUSD-954: Add "Paid by MetaMask" for subsidized Money account deposits](https://consensyssoftware.atlassian.net/browse/MUSD-954) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Paid by MetaMask tag for subsidized Money account deposits Scenario: Paid By MetaMask tag displayed on Money account deposit screen Given user has money account AND user has "no fee" token When user selects a token in the "Earn on your crypto" section that has a "no fee" token Then on Money account deposit screen the "Paid by MetaMask" tag is displayed next to the transaction fee ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> N/A - We showed `Transaction Fee: $0` ### **After** <!-- [screenshots/recordings] --> <img width="505" height="1010" alt="image" src="https://github.com/user-attachments/assets/90143fc4-3766-401b-8ee0-45e61d9c3fc9" /> ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Small confirmation UI and hook logic change with tests; no auth or payment execution paths modified. > > **Overview** > Extends **"Paid by MetaMask"** on the confirmation fee row to **Money account deposits** when all pay fees are zero and quotes exist—the same subsidized-fee behavior already used for mUSD conversion. > > `useIsPaidByMetaMask` now treats `TransactionType.moneyAccountDeposit` as supported and **returns false** when a fiat payment method is selected (`selectedPaymentMethodId` from `useTransactionPayFiatPayment`), so Apple Pay and similar flows do not show the subsidized label. Tests cover the new deposit type in `bridge-fee-row` and the fiat-payment guard in the hook. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9e02bc6. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…31482) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section blocks the PR check. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> When working on Main Navigator, I realized CollectiblesDetails route is not in used anymore. This PR is to clean up this route and the component: - Remove the legacy `CollectiblesDetails` stack screen from `MainNavigator`, which was registered but never navigated to - Delete the orphaned `CollectibleModal` component and its tests, styles, and types - Remove the unused `InitSendLocation.CollectibleModal` send analytics constant and the `CollectibleModal` CODEOWNERS entry NFT detail flows already use the `NftDetails` screen (e.g. from `NftGridItem`). The `/nft` deeplink also routes to `NFTS_FULL_VIEW`, not this modal. No in-app navigation, deeplinks, or Braze campaigns referenced `CollectiblesDetails`. <img width="1206" height="2622" alt="simulator_screenshot_123411D7-6C42-4AEE-A9F2-973B5197916D" src="https://github.com/user-attachments/assets/477730f5-a752-4682-9716-776ee46e9155" /> ## **Changelog** <!-- mms-check: type=changelog required=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry:null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: clean up CollectiblesDetails route ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: NFT details navigation after CollectiblesDetails removal Background: Given I am logged into MetaMask Mobile And I have at least one NFT in my wallet Scenario: User can still open NFT details from the wallet When I open the wallet NFT list And I tap an NFT Then I should see the NFT details screen Scenario: NFT deeplink still opens the NFT list When I open the app via the "/nft" deeplink Then I should see the wallet NFT full view Scenario: Legacy CollectiblesDetails modal is no longer reachable When I use the app normally Then I should not see the old CollectibleModal bottom sheet ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Deletion-only cleanup of unreachable navigation and UI; active NFT flows use NftDetails and other existing routes. > > **Overview** > Removes dead NFT UI: the **`CollectiblesDetails`** stack screen is unregistered from **`MainNavigator`**, and the entire **`CollectibleModal`** folder (component, styles, types, tests) is deleted. > > Also drops **`InitSendLocation.CollectibleModal`** from send analytics constants, the **`CollectibleModal`** CODEOWNERS line, and the navigator test that asserted the removed screen. NFT detail flows continue to use **`NftDetails`** and related routes already registered in the main stack. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 8610c73. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
… (TSA-657) (#31457) ## **Description** <!-- mms-check: type=text required=true --> The Quick Buy "Receive" view (sell flow) was missing the Solana, Tron, and BTC network chips, so users couldn't select those chains to receive on — breaking parity with the other supported networks. Root cause: the receive-token candidates in `useReceiveTokens` were built EVM-only — the stablecoin curation filtered `DefaultSwapDestTokens` with `chainId.startsWith('0x')`, silently dropping non-EVM (CAIP) chain ids. Since chips are derived from the candidates' chain ids, no non-EVM candidates meant no chips. All surrounding infrastructure (chain-filter display, enabled-network gating, quote flow) already supported non-EVM chains. Changes: - **Solana:** SOL native and Solana USDC are now receive candidates. - **Tron / Bitcoin:** native TRX and BTC are now receive candidates via the bridge native-asset registry. Non-native tokens on these chains (e.g. TRC-20 USDT) are deliberately excluded: `useAssetMetadata` can only resolve Solana base58 / EVM hex addresses, so they cannot resolve as Quick Buy destinations (see TSA-659). - Each non-EVM chip is gated on the selected account actually having an address on that chain (`selectSelectedInternalAccountByScope` per scope), matching app-wide gating. - `enrichSolanaTokenBalance` generalized to `enrichNonEvmTokenBalance` so Tron/BTC holdings get correct balance/fiat enrichment (previously Solana-only). - The chain filter defaults to the position's CAIP chain for non-EVM positions (previously fell back to "All"). Jira: [TSA-657](https://consensyssoftware.atlassian.net/browse/TSA-657) ## **Changelog** <!-- mms-check: type=changelog required=true --> CHANGELOG entry: Added Solana, Tron and Bitcoin network options to the Quick Buy receive view ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [TSA-657](https://consensyssoftware.atlassian.net/browse/TSA-657) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Quick Buy receive view network chips Scenario: user with a multichain account opens the receive picker Given the selected account has Solana, Tron and Bitcoin addresses And the user is selling a token via Quick Buy When the user opens the "Receive" token picker Then Solana, Tron and Bitcoin chips appear alongside the EVM network chips And selecting Tron offers native TRX as the receive option And selecting Bitcoin offers native BTC as the receive option And selecting Solana offers SOL and Solana USDC Scenario: account without non-EVM addresses Given the selected account has no Tron or Bitcoin address When the user opens the "Receive" token picker Then the Tron and Bitcoin chips are not shown ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> ### **Before** <img width="393" height="417" alt="image" src="https://github.com/user-attachments/assets/414fe115-f979-4b8c-9ef2-6d7dfca49b1f" /> ### **After** <img width="437" height="423" alt="image" src="https://github.com/user-attachments/assets/5cbf55a1-74ea-409d-aa22-6e6058a49b11" /> ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- Generated with the help of the pr-description AI skill --> [TSA-657]: https://consensyssoftware.atlassian.net/browse/TSA-657?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches sell-flow receive token selection and balance/fiat enrichment across multichain accounts; wrong gating or chain-id mismatches could hide options or misprice rows, but changes are scoped to Quick Buy with broad test coverage. > > **Overview** > Fixes the Quick Buy **Receive** picker (sell flow) so **Solana, Tron, and Bitcoin** network chips and tokens appear when the user’s account and enabled networks allow it. > > **Receive candidates (`useReceiveTokens`):** Stablecoin curation no longer drops non-EVM chains—**Solana USDC** is included alongside EVM stables. **Native TRX and BTC** are added via `NATIVE_ONLY_NON_EVM_CHAINS` (TRC-20 stables on Tron stay excluded because destinations can’t be resolved). Non-EVM options are gated on **`selectSelectedInternalAccountByScope`** per chain and existing network-enabled checks; multichain balances/rates are wired into enrichment. > > **Balances (`enrichTokenBalance`):** Solana-specific enrichment is generalized to **`enrichNonEvmTokenBalance`** with Tron/Bitcoin accounts so held TRX/BTC/SOL show correct fiat in the list. > > **UI filter (`QuickBuyReceiveScreen`):** The default chain filter uses the position’s **CAIP chain id** for non-EVM positions (matching candidate `chainId` format) instead of skipping non-EVM and falling back to “All”. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e4c8ac7. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## **Description**
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
The Card feature screens (`Routes.CARD.ROOT` -> `CardRoutes`) were
registered in the main navigator using the `ModalPresentationIOS`
transition preset. This caused the Card flow to be presented as an iOS
card-style modal stacked on top of the current screen, which did not
match the intended presentation for these screens and produced
inconsistent navigation behavior.
This PR switches the Card route presentation to
`ModalSlideFromBottomIOS`, so the Card screens slide up from the bottom
consistently with the rest of the modal flows in the app. The
corresponding test mock is updated to drop the now-unused
`ModalPresentationIOS` preset.
## **Changelog**
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry: Fixed the presentation of the Card screens so they
slide up from the bottom.
## **Related issues**
Fixes: null
## **Manual testing steps**
```gherkin
Feature: Card screen presentation
Scenario: user opens the Card flow
Given the user is on a screen that can navigate to the Card feature
When the user navigates to the Card flow
Then the Card screens are presented sliding up from the bottom
And the transition is consistent with other modal flows in the app
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
N/A
### **After**
N/A
## **Pre-merge author checklist**
<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.
Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [x] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [x] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [x] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- Generated with the help of the pr-description AI skill -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Single navigation transition preset change with no auth, data, or
routing logic changes.
>
> **Overview**
> The Card stack (`Routes.CARD.ROOT` / `CardRoutes`) in `MainNavigator`
now uses **`TransitionPresets.ModalSlideFromBottomIOS`** instead of
**`ModalPresentationIOS`**, so the flow **slides up from the bottom**
rather than using the iOS card-style stacked modal.
>
> The `@react-navigation/stack` test mock in `MainNavigator.test.tsx` is
updated to expose only **`ModalSlideFromBottomIOS`**, removing the
unused **`ModalPresentationIOS`** preset.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e5882e5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…te (TSA-632) (#31460) ## **Description** <!-- mms-check: type=text required=true --> The available balances shown in the Quick Buy sheet — the "Pay with" balance (Buy) and the "Receive" balance (Sell) — only updated when a Quick Buy trade reached a terminal state, and only really on the pay-with side. Any balance change from another cause was missed: an external incoming transfer, a swap in another flow, etc. Example: buy $0.10 of USDC paying with ETH, wait for the "complete" toast — the displayed ETH balance stayed at its pre-trade value. Two stacked causes: 1. **Frozen token snapshot** — the selected pay-with / receive token is a `useState` snapshot of one entry from the Redux-driven option lists; the auto-select effect only runs while nothing is selected, so the token object (including its cached `balance` / `balanceFiat`) froze at selection time and never re-synced when the balance controllers updated Redux. 2. **One-shot RPC fetch** — `useLatestBalance` fetches the EVM balance once per token/account/`refreshKey`, and its fetched value shadows the cached fallback. Quick Buy keyed it off a Quick-Buy-specific signal, so it only re-fetched when a Quick Buy trade settled. Fix — drive **both** displayed balances off live controller state so they re-render on **any** change, regardless of origin: - Add `resolveLiveTokenBalance`, which re-reads a selected token's balance fields from the reactive option list it was picked from (`usePayWithTokens` / `useReceiveTokens`). Those lists subscribe (via `useSelector`) to `TokenBalancesController` / `AccountTrackerController` (EVM) and the multichain balances state (non-EVM), so both the source and dest balances track live controller state. Only balance fields are read — never the `sourceToken` reference passed to quote fetching — so market-data ticks can't churn quote requests. - Re-key `useLatestBalance` off the live balance itself, so any change to the underlying balance — for any reason — triggers a fresh on-chain read and re-render, independent of Quick Buy's own state. - Expose `destBalanceFiat` from the controller and drive the Sell-mode footer pill from it (previously the frozen `selectedDestStable` snapshot). - Remove the terminal-toast `incrementBridgeBalanceRefreshKey` dispatch: the live subscription already covers the settle case, so the trigger is now "balance state changed," not "Quick Buy finished." Jira: [TSA-632](https://consensyssoftware.atlassian.net/browse/TSA-632) ## **Changelog** <!-- mms-check: type=changelog required=true --> CHANGELOG entry: Fixed the Quick Buy "Pay with" and "Receive" available balances not refreshing when the underlying balance changes (a swap settling, an external transfer, a send in another flow, etc.) ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [TSA-632](https://consensyssoftware.atlassian.net/browse/TSA-632) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Quick Buy available balance freshness Scenario: pay-with balance updates when a Quick Buy swap settles Given the user notes their ETH on Base balance in the Quick Buy sheet And the user buys $0.10 of USDC on Base paying with ETH on Base When the user re-opens the Quick Buy sheet while the swap settles And the "complete" toast appears Then the displayed ETH on Base available balance updates to the post-trade value Scenario: pay-with balance updates on an external transfer Given the Quick Buy sheet is open in Buy mode with a pay-with token selected When the selected token's balance changes from outside Quick Buy (e.g. an incoming transfer) Then the displayed available balance updates without any Quick Buy action Scenario: receive balance updates on balance change (Sell mode) Given the Quick Buy sheet is open in Sell mode with a receive token selected When that token's balance changes (a sell settling, or an external transfer) Then the displayed receive available balance updates to the new value ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> ### **Before** **🔊 Sound ON** https://github.com/user-attachments/assets/ddc84e7c-a6a2-4ec0-beee-b4e0a0e04d48 ### **After** **🔊 Sound ON** https://github.com/user-attachments/assets/fc8e7fe3-e869-44f8-b6b1-369a946a4209 https://github.com/user-attachments/assets/2751d57f-7a23-42b9-bc06-a2e823c6fac1 ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- Generated with the help of the pr-description AI skill --> [TSA-632]: https://consensyssoftware.atlassian.net/browse/TSA-632?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches balance display, slider max spend, and on-chain balance refresh in the trade flow; quote inputs intentionally keep frozen rates to avoid extra quote churn. > > **Overview** > Fixes **TSA-632**: Quick Buy “Pay with” and “Receive” balances no longer stick to the token snapshot taken at picker selection. > > Adds **`resolveLiveTokenBalance`** to match the selected pay-with / receive token against the reactive **`usePayWithTokens`** / **`useReceiveTokens`** lists (by `address:chainId`) and read only balance/rate fields so quote token references stay stable. **`useQuickBuyController`** feeds live source balance into **`useLatestBalance`** (with **`refreshKey`** tied to that balance), uses **`liveSourceCurrencyExchangeRate`** for fiat display, slider cap, and price migration, and exposes **`destBalanceFiat`** for sell mode. **`QuickBuyActionFooter`** shows **`destBalanceFiat`** in sell mode instead of the frozen **`selectedDestStable.balanceFiat`**. > > Tests cover **`resolveLiveTokenBalance`** and controller behavior for external transfers, rate-only updates, swap settlement, and list fallback. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d6ef296. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Xavier Brochard <xavier-brochard@users.noreply.github.com>
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Migrated `HeaderBase` to `design-system-react-native` usage. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-282 ## **Manual testing steps** ```gherkin Feature: header base update Scenario: user visits screen with modified files Given user landed on the screen When user interacts with modified component Then they behave as before. No visual regressions or functional regressions ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <img width="1206" height="2622" alt="image" src="https://github.com/user-attachments/assets/fc63c63a-1053-4338-a8c2-ec8eca1b561b" /> ### **After** <img width="1206" height="2622" alt="image" src="https://github.com/user-attachments/assets/c60cc494-0259-4dbc-be13-3e34a9449794" /> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Import-only migration with unchanged HeaderBase usage on navigation and modal headers; main risk is subtle visual or API differences between local and package HeaderBase. > > **Overview** > This PR continues the **HeaderBase** design-system pilot by sourcing it from `@metamask/design-system-react-native` instead of the local `component-library` `HeaderBase` module. > > **Navbar** drops the separate `HeaderBase` / `HeaderBaseVariant` import and pulls `HeaderBase` from the same MMDS bundle as other header-related primitives already used there. **Update needed** and **QR tab switcher** only change their import paths; JSX still uses the same props (`includesTopInset`, `twClassName`, `startAccessory` / `endAccessory`, children). > > No new screens or header behavior are introduced—this is an import consolidation aimed at parity with the canonical design-system component. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d5f4ffe. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section blocks the PR check. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Integrates the required `feature_id` parameter from [MetaMask/core#8964](MetaMask/core#8964) across Unified Swap/Bridge, Batch Sell, and Quick Buy quote flows. **Why:** `bridge-controller` now requires `FeatureId` on `fetchQuotes` and on client-supplied analytics context for unified swap/bridge events. Without it, quote fetches and some metrics payloads are incomplete or fail type-checking against the new API. **What changed:** - **Unified Swap/Bridge:** Pass `feature_id: UNIFIED_SWAP_BRIDGE` in `useUnifiedSwapBridgeContext` (quote polling context) and on client `trackUnifiedSwapBridgeEvent` call sites (`useTrackAllQuotesSortedEvent`, `GaslessQuickPickOptions`, `BridgeTokenSelector` asset tooltip). - **Batch Sell:** Pass `feature_id: BATCH_SELL` in `useBatchSellQuoteRequest` quote context. - **Quick Buy:** Add `getQuickBuyFeatureId` to map `QuickBuySheetSource` → `FeatureId` (`QUICK_BUY_TOKEN_DETAILS` vs `QUICK_BUY_FOLLOW_TRADING`); thread analytics `source` through `useQuickBuyController` and pass the resolved id to `BridgeController.fetchQuotes(params, featureId, signal)`. - **Tests:** Update/add unit tests for the above contexts and Quick Buy quote fetching. **Follow-up after core#8964 merges:** Remove `previewBuilds` / resolutions and bump to released package versions. ## **Changelog** <!-- mms-check: type=changelog required=true --> CHANGELOG entry: null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [SWAPS-4561](https://consensyssoftware.atlassian.net/browse/SWAPS-4561) Refs: [MetaMask/core#8964](MetaMask/core#8964) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: feature_id on swap/bridge quote flows Scenario: Unified Swap/Bridge still fetches quotes Given the app is built with preview bridge-controller packages (yarn install) And I open Unified Swap/Bridge from a token or the wallet swap entry point When the swap form loads and requests quotes Then quotes load without errors and the swap flow remains usable Scenario: Batch Sell still fetches quotes Given Batch Sell is available in the build When I open Batch Sell and trigger a quote request Then quotes load without errors Scenario: Quick Buy still fetches quotes from follow-trading Given Social Leaderboard is enabled When I open a trader position and tap Buy to open Quick Buy Then the Quick Buy sheet loads quotes without error Scenario: Quick Buy still fetches quotes from token details Given socialAiAssetDetailsQuickBuy is enabled When I open Token Details and tap the Quick Buy (lightning) button Then the Quick Buy sheet loads quotes without error ``` **Automated verification run locally:** ```bash yarn lint:tsc yarn jest app/components/UI/Bridge/hooks/useUnifiedSwapBridgeContext/useUnifiedSwapBridgeContext.test.ts yarn jest app/components/UI/Bridge/hooks/useBatchSellQuoteRequest/useBatchSellQuoteRequest.test.ts yarn jest app/components/UI/Bridge/hooks/useTrackAllQuotesSortedEvent/index.test.ts yarn jest app/components/UI/Bridge/hooks/useBridgeQuoteEvents/useBridgeQuoteEvents.test.tsx yarn jest app/components/Views/SocialLeaderboard/TraderPositionView/components/QuickBuy/useQuickBuyQuotes.test.ts yarn jest app/components/Views/SocialLeaderboard/TraderPositionView/components/QuickBuy/utils/getQuickBuyFeatureId.test.ts ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — analytics and controller plumbing only; no user-visible UI changes. ### **Before** N/A ### **After** <img width="685" height="93" alt="Screenshot 2026-06-08 at 5 29 57 PM" src="https://github.com/user-attachments/assets/17109fe7-2203-4379-8b07-042da3401d5d" /> <img width="737" height="155" alt="Screenshot 2026-06-08 at 5 30 15 PM" src="https://github.com/user-attachments/assets/2ab8f297-2eb2-4eb7-adc8-a7e9c0eb37fe" /> <img width="685" height="181" alt="Screenshot 2026-06-08 at 5 30 26 PM" src="https://github.com/user-attachments/assets/fb0bbd0a-a2c9-458c-bdac-3c0849e3e251" /> <img width="520" height="195" alt="Screenshot 2026-06-08 at 5 30 54 PM" src="https://github.com/user-attachments/assets/211396df-29f6-47db-9663-4931b1c3605c" /> <img width="587" height="163" alt="Screenshot 2026-06-08 at 5 31 14 PM" src="https://github.com/user-attachments/assets/84a4cda8-6dae-41d3-aed0-824dd62633f6" /> <img width="683" height="89" alt="Screenshot 2026-06-08 at 5 31 29 PM" src="https://github.com/user-attachments/assets/9c9b1c9b-af89-4078-b43d-3a7322b89781" /> <img width="703" height="154" alt="Screenshot 2026-06-08 at 5 31 36 PM" src="https://github.com/user-attachments/assets/983088e4-3265-4251-80b1-bdfcdc0e956d" /> ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [SWAPS-4561]: https://consensyssoftware.atlassian.net/browse/SWAPS-4561?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches quote fetching and analytics context across Unified Swap/Bridge, Batch Sell, and Quick Buy; behavior should be unchanged aside from attribution, but a wrong or missing id could break typing or mislabel metrics after the controller API change. > > **Overview** > Bumps **`@metamask/bridge-controller`** (and related lockfile entries) so quote and analytics APIs can require a **`FeatureId`**, then threads that id through swap/bridge, batch sell, and Quick Buy. > > **Unified Swap/Bridge** adds `feature_id: FeatureId.UNIFIED_SWAP_BRIDGE` to shared quote context (`useUnifiedSwapBridgeContext`) and to client `trackUnifiedSwapBridgeEvent` payloads (quotes sorted, gasless keypad input, token asset tooltip). **Batch Sell** adds `feature_id: FeatureId.BATCH_SELL` on batch quote request context. > > **Quick Buy** introduces `getQuickBuyFeatureId` to map sheet entry `source` to `QUICK_BUY_TOKEN_DETAILS` vs `QUICK_BUY_FOLLOW_TRADING`, passes analytics `source` from `useQuickBuyController`, and calls `BridgeController.fetchQuotes(params, featureId, signal)` instead of a string literal. Unit tests are updated across these flows, including abort-signal argument index changes for the new `fetchQuotes` signature. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d06c3e0. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description**
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
Product analytics need visibility into how users enter and complete the
Money Account ↔ Card linkage flow. Today, linkage can start from several
surfaces (Money Home action row, onboarding card, MetaMask card section,
Card Home, Spending Limit promo, link sheet) but those paths were not
consistently attributed in MetaMetrics.
This PR instruments the full linkage funnel:
- **View and click events** (`CARD_VIEWED`, `CARD_BUTTON_CLICKED`) on
Money Home, Card Home, onboarding card, MetaMask card, spend-and-earn
promo, and link confirmation sheet — each tagged with `screen`,
`entrypoint`, `flow`, and contextual properties (`card_state`,
`card_type`, `mode`, `action`).
- **Lifecycle events** (`CARD_MONEY_ACCOUNT_LINKING_STARTED`,
`COMPLETED`, `FAILED`) in `useMoneyAccountCardLinkage`, including
failure reasons and revoke detection.
- **Entry point propagation** through navigation params and a typed
Redux pending state (`pendingMoneyAccountCardLink: CardEntryPoint |
null`) so the origin entry point survives the post-authentication resume
path. The link sheet reports both `entrypoint` (sheet) and
`origin_entrypoint` (where the user started).
New enums in `app/components/UI/Card/util/metrics.ts`: `CardEntryPoint`,
`CardFlow`, `CardLinkingFailureReason`, plus additional `CardScreens`
and `CardActions` values.
## **Changelog**
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry: null
## **Related issues**
Fixes: null
## **Manual testing steps**
```gherkin
Feature: Money Account Card linkage analytics
Background:
Given I am logged into MetaMask Mobile
And I have a Money Account with a positive balance
And the Card feature is enabled for my region
Scenario: user starts linkage from Money Home MetaMask card
Given I am on the Money Home screen
And I see the MetaMask Card section in link mode
When user taps "Link card"
Then the link confirmation sheet should open
And confirming should start the linkage flow
Scenario: user starts linkage from Money onboarding card step
Given I am on the Money Home screen
And the onboarding card is visible on step 2 (Get a card)
When user taps the primary CTA on the onboarding card
Then the app should route into the card linkage flow
And the flow should resume after authentication if required
Scenario: user starts linkage from Card Home money account card
Given I am a cardholder on Card Home
And the Money Account card section is shown in link mode
When user taps "Link card" on the money account card
Then the link confirmation sheet should open
Scenario: user starts linkage from Spending Limit spend-and-earn promo
Given I am on the Card Spending Limit screen
And the spend-and-earn promo is visible
When user taps "Use Money account"
Then the Money Account should be selected as the spending source
Scenario: user confirms linkage in the link card sheet
Given the link confirmation sheet is open
And the sheet was opened from a tracked entry point
When user taps the confirm CTA
Then the sheet should dismiss
And the linkage should proceed in the background with success or error toast feedback
Scenario: user cancels linkage in the link card sheet
Given the link confirmation sheet is open
When user taps the close button
Then the sheet should dismiss
And no on-chain linkage should be initiated
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
N/A
### **After**
N/A
## **Pre-merge author checklist**
<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.
Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [x] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [x] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [x] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches post-auth linkage resume Redux state and delegation analytics
hooks; on-chain behavior is unchanged, but mis-attributed entrypoints
could skew funnel data.
>
> **Overview**
> Adds **MetaMetrics attribution** for the Money Account ↔ Card linkage
funnel across Money Home, Card Home, onboarding, spend-and-earn promo,
and the link confirmation sheet.
>
> **View/click events** (`CARD_VIEWED`, `CARD_BUTTON_CLICKED`) are wired
with shared `screen`, `entrypoint`, `flow`, and context (`card_state`,
`mode`, `card_type`, `action`). Impressions are **gated** until card
home data is settled (`analyticsReady` / `selectCardHomeDataStatus`).
>
> **Linkage lifecycle** events (`CARD_MONEY_ACCOUNT_LINKING_STARTED`,
`COMPLETED`, `FAILED`) run in `useMoneyAccountCardLinkage`, including
failure reasons, revoke detection, and balancing failed events when
linkage aborts mid-flight.
>
> **Entry point propagation** replaces the boolean
`pendingMoneyAccountCardLink` with `CardEntryPoint | null`, passes
`entrypoint` through `startLinkFlow`, link sheet route params, and
`confirmLinkInBackground`; the sheet reports `origin_entrypoint` vs
sheet `entrypoint`. Pending resume state is **excluded from
redux-persist**.
>
> New taxonomy lives in `metrics.ts` (`CardEntryPoint`, `CardFlow`,
`CardLinkingFailureReason`, `deriveCardState`).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f059ed1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…o prevent Earn onboarding crash (#31475) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section blocks the PR check. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Removed the global `RiveRenderer.defaultRenderer(..., RiveRendererAndroid.Canvas)` call in `FoxLoader` during app init. Although this lived in the splash-screen loader, `defaultRenderer` is a process-wide setting that forced every Rive view in the app onto Android's Canvas renderer `Surface.lockCanvas path`. This caused the Money/Earn onboarding stepper to hard-crash `SIGABRT`/`SIGSEGV` on Android when the Rive view tore down during tab navigation, the worker thread called `lockCanvas` on an already-released Surface, leaving a pending JNI exception that ART aborted on. Tombstone confirmed on Pixel 6a / Android 16. Reverting to the default Rive GPU renderer removes the `lockCanvas` code path entirely. iOS is unaffected (already uses the GPU renderer). <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- mms-check: type=changelog required=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixed an Android crash when entering Earn onboarding caused by the Rive Canvas renderer being forced app-wide. ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: #31166 #31167 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Earn onboarding no longer crashes on Android Scenario: User enters Earn onboarding on Android after fresh install Given the app is built in release mode on a Pixel 6a (or equivalent) running Android 16 And the user has completed initial app load (FoxLoader splash animation plays) When the user taps the Money/Earn tab And the Rive onboarding stepper mounts Then the app does not crash And the onboarding animation renders and steps through normally Scenario: Splash animation still plays on Android Given a fresh launch on Android When the FoxLoader splash screen appears Then the fox Rive animation plays to completion without visual regressions ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> https://github.com/user-attachments/assets/32c75ebd-8a93-4afb-ae8b-250fe4540b4f ### **Before** ### Crash on open https://github.com/user-attachments/assets/75339490-e41f-4c42-9853-c6bce7baaffc ### Crash on close https://github.com/user-attachments/assets/8fe061e9-6e5d-4c21-8d46-06082783de90 ### **After** https://github.com/user-attachments/assets/32c75ebd-8a93-4afb-ae8b-250fe4540b4f ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Small splash-only change with process-wide rendering impact on Android; fixes a production crash but may reintroduce any original Canvas-renderer motivation (e.g. splash geometry). > > **Overview** > Removes the **Android-only** `RiveRenderer.defaultRenderer(..., RiveRendererAndroid.Canvas)` setup from `FoxLoader` so Rive is no longer forced app-wide onto the Canvas renderer during splash init. > > That global override affected every Rive view (not just the fox splash), which led to **SIGABRT/SIGSEGV** when Earn/Money onboarding Rive UI tore down during tab navigation (`lockCanvas` on a released Surface). **iOS is unchanged**; Android again uses Rive’s default GPU renderer. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a212356. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…31489) ## **Description** Part of parent epic **PRED-834 — IA & Navigation Overhaul**. **Reason for the change** The Predict home redesign introduces multiple config-driven feeds (sports, politics, crypto, live, trending, popular-today). Until now there was no single generic route to render an arbitrary feed, and `handlePredictUrl` only knew about market detail links and the dedicated World Cup flow. We need one reusable route plus deeplink support so `feed=<id>` links open the correct feed and so home sections can navigate to a full feed via their "See all" affordance. **What changed** - Added a generic, typed route `Routes.PREDICT.FEED` and registered its `Stack.Screen` (rendered by the existing config-driven `PredictFeedView`). - Extended `handlePredictUrl` so that: - `feed=world-cup` continues to route to the dedicated World Cup flow (unchanged). - Known generic feed IDs route to `PredictFeedView` (gated behind the `predictHomeRedesign` flag). - Unknown feeds and the flag-off case fall back gracefully to the Predict home/market list. - `tab`, `filter`, and `q`/`query` are parsed; `filter` is parsed separately from `tab` (`tab -> initialTabId`, `filter -> initialFilterId`); `utm_source` and existing `market`/`marketId` links are preserved. - Wired the Live Now section "See all" chevron to navigate to the generic feed (`feedId: 'live'`), establishing the reference pattern for the other home sections. - Refactored the deeplink handler's raw tab field from the World-Cup-specific `worldCupTab` to a neutral `rawTab` shared by both the World Cup and generic-feed paths (decoupling). - Updated `docs/readme/deeplinking.md` to document the now-supported predict params. **Constraints / trade-offs** - Generic feed deeplinks are intentionally flag-gated on `predictHomeRedesign`; while the flag is off they fall back to the market list, so nothing is exposed prematurely during rollout. ## **Changelog** <!-- Feature is gated behind the `predictHomeRedesign` flag and is part of the larger, not-yet-released home redesign epic. --> CHANGELOG entry: null ## **Related issues** Refs: PRED-834 Fixes: <!-- TODO: add the GitHub issue / ticket number for this sub-task --> ## **Manual testing steps** > Generic-feed deeplinks require the `predictHomeRedesign` feature flag to be enabled. World Cup and market links work regardless of the flag. ```gherkin Feature: Generic Predict feed route and deeplinks Scenario: Known generic feed deeplink opens PredictFeedView (flag on) Given the predictHomeRedesign flag is enabled When the user opens the deeplink "/predict?feed=sports&tab=all&filter=live" Then the app opens PredictFeedView for the sports feed And the "all" tab and the "live" filter are pre-selected Scenario: Search deeplink opens the feed with the search overlay Given the predictHomeRedesign flag is enabled When the user opens the deeplink "/predict?feed=trending&q=bitcoin" Then the app opens PredictFeedView for the trending feed And the search is pre-populated with "bitcoin" Scenario: World Cup deeplink is unchanged When the user opens the deeplink "/predict?feed=world-cup&tab=live" Then the app opens the dedicated World Cup flow on the live tab Scenario: Unknown feed falls back gracefully Given the predictHomeRedesign flag is enabled When the user opens the deeplink "/predict?feed=not-a-real-feed" Then the app falls back to the Predict home/market list Scenario: Generic feed falls back when the flag is off Given the predictHomeRedesign flag is disabled When the user opens the deeplink "/predict?feed=sports" Then the app falls back to the Predict home/market list Scenario: Existing market deeplink still works When the user opens the deeplink "/predict?market=23246&utm_source=test" Then the app opens the market details for that market Scenario: Live Now "See all" navigates to the generic feed Given the predictHomeRedesign flag is enabled and the Predict home is visible When the user taps "See all" on the Live Now section Then the app opens PredictFeedView for the "live" feed ``` ## **Screenshots/Recordings** ### **Before** <!-- TODO: recording of feed= deeplink falling back to market list, and Live Now "See all" non-interactive --> ### **After** <!-- TODO: recording of feed= deeplinks opening PredictFeedView with tab/filter/search applied, and Live Now "See all" navigating to the live feed --> https://github.com/user-attachments/assets/043c5453-6038-4352-9d0f-149ccdb1fcdc ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - [x] I've tested with a power user scenario - [x] I've instrumented key operations with Sentry traces for production performance metrics ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ## Files changed | File | Change | | --- | --- | | `app/constants/navigation/Routes.ts` | Added `PREDICT.FEED: 'PredictFeed'` route constant | | `app/components/UI/Predict/routes/index.tsx` | Registered the `PredictFeedView` `Stack.Screen` for `Routes.PREDICT.FEED` | | `app/core/DeeplinkManager/handlers/legacy/handlePredictUrl.ts` | Parse `filter` separately; flag-gated generic-feed routing; `worldCupTab` -> `rawTab`; updated TSDoc | | `app/components/UI/Predict/views/PredictHome/components/PredictLiveNowSection/PredictLiveNowSection.tsx` | Wired "See all" to navigate to the generic feed (`feedId: 'live'`) | | `docs/readme/deeplinking.md` | Documented predict params (`feed`/`tab`/`filter`/`q`/`query`) | | `app/core/DeeplinkManager/handlers/legacy/__tests__/handlePredictUrl.test.ts` | Generic-feed, tab/filter, fallback, query, and World Cup compatibility tests | | `app/components/UI/Predict/routes/index.test.tsx` | `Routes.PREDICT.FEED` registration test | | `app/components/UI/Predict/views/.../PredictLiveNowSection.test.tsx` | "See all" navigation test | ## Acceptance criteria coverage - [x] Generic feed route is typed and registered - [x] Deeplinks with known generic feed IDs open `PredictFeedView` - [x] `filter` query param is parsed separately from `tab` - [x] `feed=world-cup` still opens the dedicated World Cup flow - [x] Unknown feeds gracefully fall back to Predict home/market list - [x] Existing query/search and market deeplinks still work - [x] Tests updated (`handlePredictUrl.test.ts`, route registration); manual QA pending recordings <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches deeplink routing and entry-point analytics for Predict; behavior is gated behind `predictHomeRedesign` with market-list fallbacks, but incorrect feed/tab mapping could misroute users from external links. > > **Overview** > Adds a **generic Predict feed** navigation path so config-driven feeds (sports, live, trending, etc.) can open in one screen, including from deeplinks and home “See all” actions. > > **Navigation:** New `Routes.PREDICT.FEED` registers `PredictFeedView` on the Predict stack. **Live Now** on Predict home is no longer a static header—the section header is pressable and navigates to `PredictFeedView` with `feedId: 'live'` and home-section analytics. > > **Deeplinks (`handlePredictUrl`):** Parses `filter` separately from `tab` (`tab` → `initialTabId`, `filter` → `initialFilterId`). When `predictHomeRedesign` is on and `feed` is a known generic id, navigation goes to `PredictFeedView` (with optional `q`/`query` and `utm_source` on `entryPoint`). `feed=world-cup` still uses the World Cup flow; unknown feeds or flag-off fall back to the market list; `market` still wins over `feed`. Deeplink docs list the new predict query params. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7353b40. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Integrates Appium smoke tests into CI alongside existing Detox smoke jobs. - Adds **Playwright + Appium** smoke runner (`tests/smoke-appium/`, `tests/playwright.smoke-appium.config.ts`) with **SmokeAccounts** tag selection aligned to smart E2E. - **PR CI (Android only):** `appium-smoke-gate` + `appium-smoke-tests-android` in `ci.yml` after `build-android-apks` (Namespace `metamask-android-build` profile). Respects smart E2E tag selection via `scripts/e2e/appium-smoke-tags.mjs`. - **Main regression (iOS):** runs on `main` every 6 hours (4×/day). Builds iOS E2E artifact, prebuilds WDA in parallel, then runs `run-appium-smoke-tests-ios.yml` with all smoke tags. - iOS runner prep (`prepare-ios-appium-runner.mjs`): sim boot, WDA prebuild/cache, `simctl` install, optional WDA warm-up. Failure screen recordings, JUnit/HTML reports on both platforms. - Extends the Playwright/Appium framework: local emulator/sim boot, Appium server reuse (`SKIP_APPIUM_STOP`), Android cold-boot stabilization (AOSP image, ANR handling), iOS WDA preinstall path, encapsulated page-object branches for shared Detox flows. - Detox Jest config ignores `tests/smoke-appium/`; Appium smoke jobs are **not** merge-blocking (`check-all-jobs-pass` excludes them). Evidence of video recording working: https://github.com/user-attachments/assets/259296b5-aa48-4294-85a9-bb4ad312ae14 https://github.com/user-attachments/assets/37585d75-c37b-4bb8-af8b-8529fe26e505 ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: N/A — CI / test infrastructure only (no product ticket). ## **Manual testing steps** ```gherkin Feature: Appium smoke CI Scenario: SmokeAccounts SRP reveal runs on Android PR CI Given a PR triggers CI with Appium smoke enabled When the Android Appium smoke job runs on the Namespace runner Then the SmokeAccounts SRP reveal spec passes (or retries then passes) And failure screen recordings are uploaded when a test fails Scenario: SmokeAccounts SRP reveal runs on iOS scheduled regression Given main has run-appium-smoke-tests-ios-scheduled.yml When the scheduled workflow runs (or is triggered via workflow_dispatch) Then the iOS build, WDA prebuild, and SmokeAccounts spec complete And failure screen recordings are uploaded when a test fails ``` Local: `yarn appium-smoke:android` / `yarn appium-smoke:ios` with a debug E2E build. Manual iOS regression: **Actions → Appium Smoke Tests (iOS — main regression) → Run workflow** (after merge to `main`). ## **Screenshots/Recordings** N/A — CI failure screen recordings are uploaded as workflow artifacts (`appium-smoke-videos-*`) when a test fails. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Large CI and test-infrastructure change (new workflows, emulator/WDA paths) with limited product code impact; Appium smoke is gated and non-merge-blocking, but failures could add CI noise and runner cost. > > **Overview** > Adds **Playwright + Appium** mobile smoke testing to CI and the local dev workflow, parallel to existing Detox smoke jobs. > > **CI:** PR pipeline gets `appium-smoke-gate` (via `appium-smoke-tags.mjs` + smart E2E tags) and **Android** `appium-smoke-tests-android` after APK build. New reusable workflows run Playwright with `tests/playwright.smoke-appium.config.ts`, shard by smoke tag (initially **SmokeAccounts**), and publish JUnit/HTML plus failure videos. **iOS** runs on a **6-hour schedule** on `main` with build + WDA prebuild (`prebuild-wda-ios.yml`, `prepare-ios-appium-runner.mjs`) before tests. `setup-e2e-env` gains optional `install-applesimutils` (off for Appium) and fixes Android SDK `chown` for `sdkmanager`. > > **Runner / framework:** Implements local emulator/sim boot, Android cold-boot stabilization (ANR handling, package trimming), Appium server reuse (`SKIP_APPIUM_STOP`), iOS WDA cache/preinstall, failure screen recording, and cross-framework gestures/assertions in shared page objects and flows. Detox Jest ignores `tests/smoke-appium/`; Appium jobs are **not** merge-blocking in `check-all-jobs-pass`. > > **Local:** `yarn appium-smoke:android` / `yarn appium-smoke:ios`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e98dd70. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cmd-ob <cmd-ob@users.noreply.github.com>
…tionDetails to HeaderStandard (#31443) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section blocks the PR check. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> 1. Migrates ConfirmAddAsset and DeFiProtocolPositionDetails from React Navigation stack headers to in-screen HeaderStandard components, aligning them with the ongoing header migration pattern. 2. Fixes ActivityView safe-area handling so the header no longer shifts when switching between back-button and default header states. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 3. What is the improvement/solution? --> ## **Changelog** <!-- mms-check: type=changelog required=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry:null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: Migrate ConfirmAddAsset and DeFiProtocolPositionDetails screens to HeaderStandard ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: HeaderStandard migration Scenario: Confirm add asset shows header and back works Given the user is on the confirm add asset screen Then the add asset title is displayed in the header When the user taps the back button Then the user returns to the previous screen Scenario: Confirm add asset import still works Given the user is on the confirm add asset screen with tokens selected When the user taps import Then the user is navigated to the wallet screen Scenario: DeFi protocol position details shows back button and content Given the user is on the DeFi protocol position details screen Then the protocol name is displayed And a back button is displayed When the user taps the back button Then the user returns to the wallet screen Scenario: Activity header is stable and respects safe area Given the user is on the Activity screen Then the activity title is displayed in the header And the header does not shift on load Scenario: Activity back button navigates home when Money account is enabled Given the Money account feature is enabled And the user is on the Activity screen When the user taps the back button Then the user is navigated to the home screen ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> iOS |BEFORE|AFTER| |---|---| |<img width="366" height="752" alt="Before" src="https://github.com/user-attachments/assets/ef0d7359-0c50-4409-8931-98e227493cfa" />|<img width="366" height="752" alt="After" src="https://github.com/user-attachments/assets/54356e8c-c515-4fa7-9d2c-260aa68201fa" />| Android https://github.com/user-attachments/assets/7fa36b5d-ca9d-404a-b680-372aaeb5ca32 ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > UI and navigation presentation only; no auth, transactions, or data-layer changes, with unit test coverage for back navigation and safe areas. > > **Overview** > Continues the **HeaderStandard** migration by hiding stack headers for **ConfirmAddAsset** and **DeFiProtocolPositionDetails** and rendering headers inside each screen instead. > > **ConfirmAddAsset** drops `navigation.setOptions` / `getHeaderCompactStandardNavbarOptions` and adds an in-screen `HeaderStandard` with the add-asset title and `goBack`, wrapped in `SafeAreaView` with left/right/bottom edges only. > > **DeFiProtocolPositionDetails** replaces the stack navbar (`getDeFiProtocolPositionDetailsNavbarOptions`) with `HeaderStandard` (empty title, `navigation.pop` on back), `SafeAreaView` + `includesTopInset`, and a content wrapper style; the removed navbar helper and its tests are deleted from `Navbar`. > > **ActivityView** changes `SafeAreaView` from top additive edges to left/right/bottom only and sets **`includesTopInset`** on both `HeaderStandard` and `HeaderRoot` so top inset is handled by the header and the title no longer jumps when toggling back vs default header. > > Navigator and component tests are updated for `headerShown: false` and the new back/safe-area behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0170d11. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 : )