[pull] main from MetaMask:main#335
Merged
Merged
Conversation
…22832) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** - Improving copy on positions that have resolved early (so it's no long incorrectly showing things like "Ended in 14 days" and now says "Resolved early" <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: [PRED-284](https://consensyssoftware.atlassian.net/browse/PRED-284) ## **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="420" alt="Simulator Screenshot - iPhone 16 Pro Max - 2025-11-17 at 11 01 16" src="https://github.com/user-attachments/assets/e309867e-0fa7-46f0-a41a-a5cb14e28acc" /> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] 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. [PRED-284]: https://consensyssoftware.atlassian.net/browse/PRED-284?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Updates `PredictPositionResolved` to use i18n and display "Resolved early" when `endDate` is in the future; adjusts tests and adds locale strings. > > - **Predict UI**: > - Replace hardcoded copy with i18n in `PredictPositionResolved` (amount/outcome, won/lost, ended/resolved-early). > - Add `formatMarketEndDate` using `dayjs.isAfter` to show `"Resolved early"` for future `endDate`; otherwise `"Ended" <relative time>`. > - **Tests**: > - Update `PredictPositionResolved.test.tsx` to mock i18n and dayjs, and assert via regex against localized strings. > - **Localization**: > - Add `predict.market_details.ended` and `predict.market_details.resolved_early` keys to `en.json`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c2f46af. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## Version Bump After Release This PR bumps the main branch version from 7.60.0 to 7.61.0 after cutting the release branch. ### Why this is needed: - **Nightly builds**: Each nightly build needs to be one minor version ahead of the current release candidate - **Version conflicts**: Prevents conflicts between nightlies and release candidates - **Platform alignment**: Maintains version alignment between MetaMask mobile and extension - **Update systems**: Ensures nightlies are accepted by app stores and browser update systems ### What changed: - Version bumped from `7.60.0` to `7.61.0` - Platform: `mobile` - Files updated by `set-semvar-version.sh` script ### Next steps: This PR should be **manually reviewed and merged by the release manager** to maintain proper version flow. ### Related: - Release version: 7.60.0 - Release branch: release/7.60.0 - Platform: mobile - Test mode: false --- *This PR was automatically created by the `create-platform-release-pr.sh` script.* Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
…r system (#22566) Fixes: https://consensyssoftware.atlassian.net/jira/polaris/projects/MWMR/ideas/view/8845676?selectedIssue=MWMR-10 ## **Description** This PR implements the handler layer and integration for the Universal Router system, building on the core infrastructure from Phase 2 PR 1. It introduces a declarative, handler-based approach for processing deep links, replacing the need to modify core routing logic when adding new deep link types. ### What Changed? Added handler implementation and integration layer to `app/core/DeeplinkManager/router/`: 1. **BaseHandler** (`handlers/BaseHandler.ts` - 103 lines) - Common utilities for all handlers (navigation, auth, error handling, analytics) - Parameter validation helpers - Success/error result factory methods 2. **NavigationHandler** (`handlers/NavigationHandler.ts` - 75 lines) - Handles simple navigation deep links (HOME, CREATE_ACCOUNT, REWARDS, PREDICT, PERPS) - Priority: 10 (standard) 3. **SwapHandler** (`handlers/SwapHandler.ts` - 67 lines) - Handles token swap deep links - Priority: 50 (high priority for core functionality) - Authentication-aware with default slippage settings 4. **SendHandler** (`handlers/SendHandler.ts` - 78 lines) - Handles transaction/send deep links - Delegates APPROVE actions to legacy system (requires complex transaction creation) - Parameter validation for required fields 5. **UniversalRouterIntegration** (`integration/UniversalRouterIntegration.ts` - 80 lines) - Integration wrapper for easy adoption into existing deeplink system - Feature flag checking (`MM_UNIVERSAL_ROUTER`) - Context creation from existing DeeplinkManager 6. **Updated UniversalRouter** (~20 lines added) - Registers all handlers in `initialize()` method - Imports and instantiates concrete handler classes ### Why? Completes the Universal Router architecture started in Phase 2 PR 1 by: - ✅ **Enabling handler-based routing**: New deep link types can be added without modifying core logic - ✅ **Isolated testing**: Each handler can be tested independently - ✅ **Clear separation**: Routing logic separated from handler implementation - ✅ **Feature-flagged**: Safe gradual rollout with automatic fallback to legacy ### Trade-offs - **No BrowserHandler yet**: Kept scope minimal. Browser/dApp links continue using legacy system for now. - **APPROVE delegates to legacy**: Transaction creation is complex and tightly coupled to legacy system. Keeping it delegated reduces risk. - **String route names**: Used string literals instead of Routes constants in some places to avoid circular dependencies. ## **Changelog** CHANGELOG entry: null _(Internal infrastructure - no end-user-facing behavior changes. Feature flag is OFF by default.)_ ## **Related issues** Refs: Deep Link Consolidation Epic (Phase 2 PR 2) ## **Manual testing steps** Feature: Universal Router Handler System Scenario: Router remains inactive when feature flag is OFF Given the app is running with MM_UNIVERSAL_ROUTER flag disabled And handlers are registered in the UniversalRouter When a deep link is opened (e.g., metamask://home) Then the deep link should be handled by the legacy system And no errors should occur Scenario: Router processes links when feature flag is ON Given the app is running with MM_UNIVERSAL_ROUTER flag enabled And NavigationHandler is registered for HOME action When a deep link is opened (metamask://home) Then the UniversalRouter should route to NavigationHandler And the handler should navigate to home screen And analytics should track the handler success Scenario: Router falls back to legacy for unhandled actions Given the MM_UNIVERSAL_ROUTER flag is enabled And no handler is registered for a specific action When a deep link with unhandled action is opened Then the router should delegate to legacy system And no errors should occur Scenario: Authentication check blocks unauthenticated users Given the MM_UNIVERSAL_ROUTER flag is enabled And the wallet is locked When a deep link requiring authentication is opened (e.g., metamask://swap) Then the handler should detect authentication is required And should delegate to legacy system### Testing Commands # Run all router tests yarn jest app/core/DeeplinkManager/router/ # Run ESLint yarn eslint app/core/DeeplinkManager/router/ # Run TypeScript check yarn lint:tsc | grep "app/core/DeeplinkManager/router"## **Screenshots/Recordings** N/A - Infrastructure change with no UI impact ## **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 (37 passing unit tests) - [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] > Adds handler-based routing (navigation, swap, send), registers them in `UniversalRouter`, and integrates behind a feature flag with comprehensive tests. > > - **Router** > - `UniversalRouter.initialize()` now clears registry and registers `SwapHandler`, `SendHandler` (priority 50) and `NavigationHandler` (priority 10); throws on registration failure. > - Routing delegates to legacy when no handler matches or when a handler requests `fallbackToLegacy`. > - **Handlers** > - Add `handlers/BaseHandler` with navigation/auth/analytics helpers and result/param utilities. > - Add `handlers/NavigationHandler` for actions like `HOME`, `CREATE_ACCOUNT`, `REWARDS`, `PREDICT`, `PERPS*`. > - Add `handlers/SwapHandler` and `handlers/SendHandler` with auth checks; `APPROVE` delegates to legacy. > - Export all via `handlers/index`. > - **Integration** > - Add `integration/UniversalRouterIntegration` to gate usage via `MM_UNIVERSAL_ROUTER` and construct handler context; exposes `processWithNewRouter` and `getRouter`. > - **Interfaces/Utils** > - Update `HandlerContext.instance` typing and docs; add optional lifecycle hooks; add `testUtils`. > - **Tests** > - New unit tests for handlers and integration; update `UniversalRouter.test` (init failure case, priority order, legacy delegation). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7d8fe2d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…22792) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** The scrollable section in the select assets list stops scrolling prematurely, preventing some assets at the bottom from being displayed. Users are unable to select these assets as the list snaps back to a previous position when trying to scroll to the end. This issue needs to be resolved before the new asset picker goes live. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixes scrolling issue in asset list that prevents rendering bottom assets ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-3453 ## **Manual testing steps** ```gherkin Ensure that assets that appear at the bottom of the asset list in both src and dest asset selector are visible in both android and ios. ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> https://github.com/user-attachments/assets/31ccef83-f961-4785-b763-4151c9c8f229 ### **After** <!-- [screenshots/recordings] --> https://github.com/user-attachments/assets/8d1bbe3c-ef73-41c0-9732-2f7f7e687530 ## **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] > Make bridge token lists occupy full height and remove extra bottom padding to prevent premature scroll stops. > > - **Bridge token selectors (`BridgeTokenSelectorBase`)**: > - Add `tokensListContainer` with `flex: 1` and set `tokensList` `flex: 1` to ensure the list fills available space. > - Add `tokensListContent` with `paddingBottom: 0` and pass via `contentContainerStyle` to avoid extra bottom space. > - **Tests**: > - Update snapshots for `BridgeSourceTokenSelector` and `BridgeDestTokenSelector` to reflect new layout and list content container styles. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7e45658. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…rns (#22874) ## **Description** This PR refactors the `TokenSelection` screen to align with standard Ramp patterns and prepares it for implementing dynamic token fetching (TRAM-2820). ### Key Changes: 1. **Standardized Navigation Pattern** - Replaced custom navigation setup with `getDepositNavbarOptions()` (consistent with all other Ramp screens) - Added `createTokenSelectionNavDetails()` export for type-safe navigation - Removed `rampType` from params, now derives routing decision from Redux state via `getRampRoutingDecision` 2. **Updated Parameter Structure** - Changed from `{ rampType, selectedCryptoAssetId }` to `{ intent }` - Now uses `RampIntent` type which includes `assetId` for pre-selection - Aligns with Aggregator flow's intent-based navigation pattern 3. **Layout Component Migration** - Replaced `SafeAreaView` with `ScreenLayout` component - Uses the same layout pattern as all Aggregator and Deposit screens - Provides consistent safe area handling and background styling 4. **Style Migration to Tailwind CSS** - Replaced `StyleSheet.create()` with Tailwind `twClassName` props - Migrated `View` components to `Box` from design system - Removed `TokenSelection.styles.ts` file entirely - Simplified styling: `py-2`, `px-4 py-3`, `flex-1` 5. **Test Updates** - Added mock for `getRampRoutingDecision` selector - Updated `useParams` mock to use `intent` structure - Updated test case for token selection to use `intent.assetId` - Added `rampRoutingDecision` to test state ### Why These Changes? - **Consistency**: Now follows the exact same patterns as other Ramp screens - **Maintainability**: Using shared components means updates apply automatically - **Type Safety**: Intent-based parameters provide better TypeScript support - **Preparation**: Sets foundation for implementing dynamic token fetching from API (TRAM-2816/2820) ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: TRAM-2820 ## **Manual testing steps** ```gherkin Feature: Token Selection Screen Scenario: user views token selection screen Given user is on the token selection screen When screen loads Then user sees standard Ramp navigation header And user sees close button in top right And user sees network filter bar And user sees search input And user sees list of tokens Scenario: user searches for tokens Given user is on the token selection screen When user types "USDC" in search input Then user sees only tokens matching "USDC" Scenario: user filters by network Given user is on the token selection screen When user selects a specific network filter Then user sees only tokens on that network Scenario: user views unsupported token info Given user sees a greyed-out unsupported token When user taps the info icon Then user sees unsupported token modal ``` ## **Screenshots/Recordings** ### **Before** - Used custom navigation with `ButtonIcon` components - Used `SafeAreaView` with custom styles - Used StyleSheet for all styling - Parameter: `selectedCryptoAssetId` ### **After** - Uses `getDepositNavbarOptions()` (standard pattern) - Uses `ScreenLayout` component (standard pattern) - Uses Tailwind CSS with `twClassName` props - Parameter: `intent.assetId` _(Note: Visual appearance remains the same - this is a refactoring/preparation PR)_ ## **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] > Refactors TokenSelection to use shared ScreenLayout and intent-based params/navigation, updates tests and snapshots, and switches Solana USDC to CAIP/slip44 identifiers and icons. > > - **TokenSelection screen**: > - Migrate to `ScreenLayout` and design-system `Box`; remove `TokenSelection.styles.ts`. > - Standardize navbar via `getDepositNavbarOptions` and add `createTokenSelectionNavDetails`. > - Change route params to `intent` (`RampIntent`) and read routing via Redux `getRampRoutingDecision`. > - Hook integrations: keep `useSearchTokenResults`; add list scrolling on search. > - **Token item rendering**: > - Default `TokenListItem` `textColor` to `TextColor.Alternative`. > - **Data/constants**: > - Update Solana USDC to `solana:…/slip44:501` with matching icon URL. > - **Tests & snapshots**: > - Update to intent-based params, add `rampRoutingDecision` in state, add empty-state and unsupported-info interactions; refresh snapshots (including updated Solana USDC icon/asset). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e9c18ad. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** We only want mainnet ETH to be stakable, not ETH in other chains. <img width="398" height="520" alt="Screenshot 2025-11-17 at 10 47 47" src="https://github.com/user-attachments/assets/dc205302-cbb6-4d43-8b42-0661da19061d" /> ## **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: Only mainnet ETH should be stakable ## **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** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Restricts staking eligibility to ETH on Ethereum mainnet or Hoodi testnet and updates related tests/mocks accordingly. > > - **Stake Logic**: > - Update `selectIsStakeableToken` to allow staking for `ETH` only on mainnet (`isMainnetByChainId`) or Hoodi testnet (new `isHoodiChainId` supporting hex/decimal/CAIP forms). > - Keep TRX staking gated by `selectTrxStakingEnabled` and TRON chain check. > - **Tests & Mocks**: > - Add/adjust tests in `stakeableTokens.test.ts` for mainnet, non-mainnet, and Hoodi scenarios. > - Ensure ETH test assets include `chainId` (e.g., in `EarnBalance.test.tsx`). > - Standardize `isNonEvmChainId` mocking and preserve actual exports when mocking `util/networks` across multiple tests (`SearchTokenAutocomplete`, `Tokens`, `AddAsset`, `TokenListItem*`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 4405081. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
> > Add MetaMask relayer order submission mocks (with CLOB fallback) for
cash-out and unskip the Spurs vs. Pelicans cash-out e2e test.
>
> - **E2E Mocks (Polymarket)**
> - Add relayer order submission mock for
`predict.dev-api.cx.metamask.io/order` via `/proxy` with cash-out `SELL`
order validation and success response.
> - Adjust CLOB fallback mock for `clob.polymarket.com` to return
`success`, `orderID`, `transactionsHashes`, `takingAmount`, and
`makingAmount` (decimal strings).
> - Integrate balance update after cash-out via
`POLYMARKET_UPDATE_USDC_BALANCE_MOCKS`.
> - **E2E Test**
> - Unskip and run `should cash out on open position: Spurs vs.
Pelicans` in `e2e/specs/predict/predict-cash-out.spec.ts`, using new
mocks and asserting updated activity and balances.
## **Changelog**
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry:
## **Related issues**
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**
- [ ] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds MetaMask relayer and CLOB fallback mocks for cash-out (with
balance update), introduces prices API mock, switches Polygon RPC to
Infura proxy, and enables the Spurs vs. Pelicans cash-out e2e test.
>
> - **E2E Mocks (Polymarket)**:
> - **Relayer + CLOB (cash-out)**: Add
`/proxy?url=predict.*.metamask.io/order` mock validating `SELL` orders
and success payload; update `clob.polymarket.com` fallback to return
`success`, `orderID`, `transactionsHashes`, `takingAmount`,
`makingAmount`; trigger
`POLYMARKET_UPDATE_USDC_BALANCE_MOCKS('cash-out')`.
> - **Prices API**: New `POLYMARKET_PRICES_MOCKS` for
`clob.polymarket.com/prices` and register in
`POLYMARKET_COMPLETE_MOCKS`.
> - **Positions filtering**: Add `eventId` filtering and ensure
`proxyWallet` mapping for resolved/current positions in
`POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS` and
`POLYMARKET_RESOLVED_MARKETS_POSITIONS_MOCKS`.
> - **Fixture**:
> - `withPolygon`: Route via Infura URL through mock `/proxy` (uses
`MM_INFURA_PROJECT_ID`).
> - **E2E Test**:
> - Enable and refactor `predict-cash-out.spec.ts`: unskip test, set
mocks (`POLYMARKET_REMOVE_CASHED_OUT_POSITION_MOCKS` then
`POLYMARKET_POST_CASH_OUT_MOCKS`), assert updated balance `$58.66`,
position removal, and activity details (`Cashed out`, `$30.75`).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e126795. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…trics (#22822) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> This PR improves metrics tracking for the card delegation flow by distinguishing between actual failures and user cancellations. Previously, when users cancelled the transaction confirmation modal during the card delegation process, it was incorrectly tracked as a failed delegation (`CARD_DELEGATION_PROCESS_FAILED`). This skewed failure metrics and made it difficult to identify genuine system errors. ## **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 card delegation metrics to correctly distinguish between user cancellations and actual failures ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] 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] > Track `CARD_DELEGATION_PROCESS_USER_CANCELED` for user cancellations and only log/track failures for real errors in the card delegation flow. > > - **Card Delegation Flow**: > - Update `useCardDelegation` to emit `CARD_DELEGATION_PROCESS_USER_CANCELED` on `UserCancelledError`; only emit `CARD_DELEGATION_PROCESS_FAILED` and log errors for non-cancellation failures. > - Add `UserCancelledError` handling in approval transaction path; preserve state updates and error propagation. > - **Analytics**: > - Add `EVENT_NAME.CARD_DELEGATION_PROCESS_USER_CANCELED` and expose via `MetaMetricsEvents`. > - **Tests**: > - Extend `useCardDelegation.test.ts` to assert canceled vs failed event tracking, absence of error logging on cancel, and existing metrics properties remain unchanged. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9c66894. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
This PR fixes a race condition bug in the onboarding flow where users
typing quickly and immediately pressing submit buttons would have their
input data lost due to debounce delays.
## **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 a bug where rapidly typing in Card onboarding
forms and immediately submitting could send incomplete data to the
server
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: Onboarding form submission with rapid typing
Scenario: user types email rapidly and submits immediately in Sign Up screen
Given user is on the Sign Up screen
And all form fields are empty
When user rapidly types "test@example.com" in email field
And user rapidly types "Password123!" in password field
And user rapidly types "Password123!" in confirm password field
And user selects a country
And user immediately presses Continue button (within 1 second)
Then the API should receive "test@example.com" (not partial/stale email)
And user should navigate to Confirm Email screen
Scenario: user types phone number rapidly and submits immediately in Set Phone Number screen
Given user is on the Set Phone Number screen
And country code is selected
When user rapidly types "1234567890" in phone number field
And user immediately presses Continue button (within 1 second)
Then the API should receive "1234567890" (not partial phone number)
And user should navigate to Confirm Phone Number screen
Scenario: US user types SSN rapidly and submits immediately in Personal Details screen
Given user is on the Personal Details screen
And user has selected US as country
And first name, last name, date of birth, and nationality are filled
When user rapidly types "123456789" (9 digits) in SSN field
And user immediately presses Continue button (within 1 second)
Then the API should receive "123456789" (complete SSN, not partial)
And user should navigate to Physical Address screen
Scenario: user submits invalid data with rapid typing
Given user is on the Sign Up screen
When user rapidly types "invalid-email" in email field
And user rapidly types "weak" in password field
And user rapidly types "weak" in confirm password field
And user selects a country
And user immediately presses Continue button
Then validation errors should be shown
And API should not be called
And user should remain on Sign Up screen
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] 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]
> Use non-debounced field values for validation/submission in onboarding
forms and switch consent `policyType` from `US` to `us`.
>
> - **Onboarding UI (validation/submission)**:
> - Sign Up (`SignUp.tsx`): validate/submit using live `email`,
`password`, `confirmPassword`; update `isDisabled`; send current values
to API; tweak handlers.
> - Set Phone Number (`SetPhoneNumber.tsx`): validate `phoneNumber`
(4–15 digits) before submit; compute `isDisabled` from current value;
pass live number to API/navigation; adjust error handling.
> - Personal Details (`PersonalDetails.tsx`): require and validate `SSN`
(9 digits) for US before submit; compute `isDisabled` using live `SSN`;
send current `ssn` to API.
> - **Consent policy casing**:
> - Change consent `policyType` from `US` to `us` across code, tests,
and types (`useRegisterUserConsent.ts`,
`useRegisterUserConsent.test.ts`, `CardSDK.test.ts`, `types.ts`).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b039631. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description** Adds a new RampUnsupportedModal to inform users when buying crypto is unavailable in their region. This modal provides clear feedback when users attempt to access ramp functionality from an unsupported geographic location. **Changes:** - Created new `RampUnsupportedModal` component using design system patterns (Box with Tailwind, BottomSheet) - Registered modal route (`UNSUPPORTED_REGION_MODAL`) in navigation stack - Added localization strings for modal title, description, and CTA - Included comprehensive unit tests with snapshot coverage **Motivation:** When users from restricted regions attempt to buy crypto, they need clear communication about why the feature is unavailable. This modal provides user-friendly messaging explaining regional limitations and regulatory restrictions. ## **Changelog** CHANGELOG entry: Added unsupported region modal to inform users when buying crypto is unavailable in their location ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TRAM-2796 ## **Manual testing steps** Feature: Unsupported Region Modal Scenario: user views unsupported region modal Given user is in a region where ramp services are unavailable When user navigates to the unsupported region modal Then user should see the modal with title "Unavailable in your region" And user should see descriptive text about regional limitations And user should see a "Got It" button Scenario: user dismisses modal via close button Given user has the unsupported region modal open When user taps the close button in the header Then the modal should close And user should return to the previous screen Scenario: user dismisses modal via Got It button Given user has the unsupported region modal open When user taps the "Got It" button Then the modal should close And user should return to the previous screen ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** https://github.com/user-attachments/assets/94b2a9f2-c976-4b09-8dd3-f08611b3621f <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces `RampUnsupportedModal` with navigation support, improves eligibility modal error handling, updates tests/snapshots, and adds localization strings. > > - **UI — Ramps**: > - Adds `UI/Ramp/components/RampUnsupportedModal` (component, index, nav details) with close actions. > - Updates `EligibilityFailedModal` to `catch` `Linking.openURL` errors. > - **Navigation**: > - Adds `Routes.SHEET.UNSUPPORTED_REGION_MODAL` and registers it in `Nav/App/App.tsx` (`RootModalFlow`). > - **Tests**: > - Adds `RampUnsupportedModal.test.tsx` with snapshot and interaction tests; route registration test in `Nav/App/App.test.tsx`. > - Updates eligibility modal tests (mock `Linking.openURL`, assertion to `toBeOnTheScreen`). > - **Localization**: > - Adds `fiat_on_ramp_aggregator.unsupported_region_modal` strings in `locales/languages/en.json`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e23d610. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- 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? --> Due to the previous implementation of detecting migration failures to prompt the Vault Recovery screen causing the Vault Recovery screen to show up on a fresh install to users who had previously had the MM app installed. This is due to on iOS due to keychain not being reset on app uninstall. This method uses a flag stored in FileSystem Storage to check instead of the presence of a backed up vault. Bug: #22567 Bitrise Build: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/3efcb785-454e-4fa6-8cd3-8b8b8b224999 ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** 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** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Use a FilesystemStorage-backed migration error flag to trigger vault recovery from onboarding and clear it after successful restore; set the flag on migration failures and add corresponding tests. > > - **Onboarding**: > - Read `MIGRATION_ERROR_HAPPENED` from `FilesystemStorage`; if `'true'` and `getVaultFromBackup()` returns a vault, reset navigation to `Routes.VAULT_RECOVERY.RESTORE_WALLET`. > - Skip check in E2E or when `route.params.delete` is set; add error logging. > - **Vault Recovery (WalletRestored)**: > - On continue, remove `MIGRATION_ERROR_HAPPENED` via `FilesystemStorage.removeItem`; log failures but still navigate to `Routes.ONBOARDING.LOGIN` with `{ isVaultRecovery: true }`. > - **Migrations**: > - On any migration error, set `MIGRATION_ERROR_HAPPENED` in `FilesystemStorage` (uses `Device.isIos()` to avoid iCloud backup) before rethrowing. > - **Constants**: > - Add `MIGRATION_ERROR_HAPPENED` to `app/constants/storage.ts`. > - **Tests**: > - Update/add tests for onboarding migration check, recovery redirect conditions, error handling, and clearing the flag in `WalletRestored`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e1156ba. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: metamaskbot <metamaskbot@users.noreply.github.com>
…en (#22892) ## **Description** This PR implements **TRAM-2820**: Dynamic token fetching for the Token Selection screen in the Ramp flow, replacing hardcoded mock data with real-time API data from the on-ramp-cache endpoint. ### **What changed and why:** **Problem:** The Token Selection screen was using hardcoded `MOCK_CRYPTOCURRENCIES` data, preventing users from seeing the actual available tokens for their region and selected flow (Buy vs Deposit). **Solution:** 1. **Created `useRampTokens` hook** to fetch tokens from the on-ramp-cache API endpoint 2. **Integrated hook into TokenSelection component** with loading and error states 3. **Added network filtering** to only show tokens for networks the user has added to their wallet 4. **Implemented smart list switching** - displays curated `topTokens` by default, switches to complete `allTokens` when searching ### **Key Features:** - ✅ **Environment-aware API calls**: Uses staging or production URLs based on `METAMASK_ENVIRONMENT` - ✅ **Region-based tokens**: Fetches tokens based on user's detected geolocation - ✅ **Flow-aware**: Different token lists for Aggregator (buy) vs Deposit flows - ✅ **Network filtering**: Only shows tokens for networks in user's wallet (e.g., excludes Tron if user doesn't have Tron network) - ✅ **Smart UX**: Shows curated top tokens initially, searches across all tokens - ✅ **Loading/Error states**: Proper UI feedback during fetch and on errors - ✅ **Type-safe**: Full TypeScript support with `RampsToken` interface ### **Technical Details:** **API Endpoint:** ``` {baseUrl}/regions/{region}/tokens?action={action}&sdk=2.1.5 ``` **Mapping:** - `UnifiedRampRoutingType.AGGREGATOR` → `action=buy` - `UnifiedRampRoutingType.DEPOSIT` → `action=deposit` **Response Structure:** ```json { "topTokens": [...], // Curated list "allTokens": [...] // Complete list } ``` **Previous work included in this PR (refactoring):** - Standardized navigation using `getDepositNavbarOptions()` - Migrated to `ScreenLayout` component - Converted styles to Tailwind CSS with `twClassName` - Updated parameter structure from `selectedCryptoAssetId` to `intent.assetId` --- ## **Changelog** CHANGELOG entry: null --- ## **Related issues** Refs: [TRAM-2820](https://consensyssoftware.atlassian.net/browse/TRAM-2820) --- ## **Manual testing steps** ```gherkin Feature: Dynamic Token Fetching Scenario: user views token selection screen with real tokens Given user is in a supported region (e.g., US-CA) And user has Ethereum network in wallet When user navigates to token selection screen Then user sees loading indicator And user sees tokens fetched from API And user sees supported tokens displayed normally And user sees unsupported tokens greyed out with info icon Scenario: user searches across all available tokens Given user is on token selection screen And curated top tokens are displayed When user types in search field Then search switches to complete token list And user sees all matching tokens (supported and unsupported) When user clears search Then screen returns to showing curated top tokens Scenario: user only sees tokens for networks they have Given user has only Ethereum network in wallet And API returns tokens for Ethereum, Tron, and Solana When screen loads Then user sees only Ethereum tokens And Tron and Solana tokens are filtered out Scenario: error handling when API fails Given API is unavailable or returns error When user navigates to token selection screen Then user sees error message And user can close the screen Scenario: no region detected Given user's region cannot be detected When user navigates to token selection screen Then no API call is made And appropriate state is shown ``` --- ## **Screenshots/Recordings** ### **Before** - Used hardcoded `MOCK_CRYPTOCURRENCIES` (5 tokens only) - No loading states - No error handling - Custom navigation setup - StyleSheet-based styling ### **After** - Dynamic token fetching from API (25+ tokens) - Loading indicator while fetching - Error message on fetch failure - Standard Ramp navigation pattern - Tailwind CSS styling - Network-based filtering - Smart list switching (topTokens/allTokens) https://github.com/user-attachments/assets/fc623885-1fc7-4f5e-8b19-921511e4e255 --- ## **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. --- ## **Files Changed Summary** ### **New Files:** 1. `app/components/UI/Ramp/hooks/useRampTokens.ts` - Hook to fetch tokens from API 2. `app/components/UI/Ramp/hooks/useRampTokens.test.ts` - Comprehensive tests (24 test cases) 3. `app/components/UI/Ramp/components/TokenListItem/TokenListItem.test.tsx` - Component tests (12 test cases) ### **Modified Files:** 4. `app/components/UI/Ramp/components/TokenSelection/TokenSelection.tsx` - Integrated hook, added loading/error states 5. `app/components/UI/Ramp/components/TokenSelection/TokenSelection.test.tsx` - Updated tests for hook integration 6. `app/components/UI/Ramp/components/TokenListItem/TokenListItem.tsx` - Made `isSelected` optional, removed from usage 7. `babel.config.tests.js` - Added hook files to process.env exclude list 8. `locales/languages/en.json` - Added error message localization ### **Deleted Files:** 9. `app/components/UI/Ramp/components/TokenSelection/TokenSelection.styles.ts` - Migrated to Tailwind [TRAM-2820]: https://consensyssoftware.atlassian.net/browse/TRAM-2820?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduce useRampTokens to fetch region- and flow-aware tokens from on-ramp cache and integrate into TokenSelection with loading/error states, network filtering, and UI tweaks. > > - **Ramp tokens fetching**: > - Add `useRampTokens` hook to fetch tokens from env-aware on-ramp cache (`production`/`staging`) with `action=buy|deposit` and `sdk` version. > - Filter tokens by user-added networks; expose `topTokens`, `allTokens`, `isLoading`, `error`. > - **TokenSelection**: > - Replace mock data with `useRampTokens` output; default to `topTokens`, switch to `allTokens` when searching. > - Add loading spinner and localized error state (`deposit.token_modal.error_loading_tokens`). > - Apply network filter bar using unique chains from fetched tokens. > - Remove intent-based selection; mark unsupported via `!token.tokenSupported` and show info modal. > - **TokenListItem**: > - Make `isSelected` optional; show `token.name` primary and `token.symbol` secondary; use `depositNetworkName ?? networkName` for badge; enable info button for disabled items. > - **Tests & config**: > - Add comprehensive tests for `useRampTokens`, `TokenSelection`, and `TokenListItem` (snapshots and behaviors). > - Update `babel.config.tests.js` to avoid inlining env vars for new hook files. > - Update snapshots reflecting name/symbol swap and disabled/state changes. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 5dadc12. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
<!--
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?
-->
Fixed critical OTP input freezing issues affecting three Card
onboarding/authentication screens where users were unable to navigate,
proceed, or sometimes even focus the 6-digit OTP input field.
**Problem:**
Users experienced UI freezing on OTP verification screens, preventing
them from:
- Focusing the OTP input field
- Navigating back to previous screens
- Proceeding after entering the verification code
**Root Cause:**
The implementations were missing the `useBlurOnFulfill` hook from
`react-native-confirmation-code-field`, which is critical for proper
input lifecycle management. Without this hook:
- The input field remained focused after all digits were entered
- The keyboard wasn't properly dismissed
- Focus conflicts caused the UI to freeze
- Navigation became blocked
**Solution:**
- Added `useBlurOnFulfill` hook to properly manage OTP input focus and
blur events
- Updated ref handling to use the hook's return value instead of manual
useRef
- Fixed hook dependency arrays to prevent stale closure issues
- Fixed a cooldown timing bug in ConfirmPhoneNumber (was setting
cooldown before API success)
- Updated all test mocks to include the new hook
The implementation now matches the working pattern used in the
Ramp/Deposit OTP screen (OtpCode.tsx), which doesn't experience these
issues.
## **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 OTP input freezing issues on Card email and phone
verification screens
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] 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]
> Integrates useBlurOnFulfill for OTP fields, adds platform-specific
autocomplete, refactors focus/refs, and fixes resend cooldown timing;
updates tests and mocks accordingly.
>
> - **Card OTP flows**
> - Use `useBlurOnFulfill` for `CodeField` in `CardAuthentication`,
`ConfirmEmail`, and `ConfirmPhoneNumber` to manage focus/blur and
prevent freezes.
> - Replace manual `useRef` with hook-provided refs; update focus
effects and dependency arrays.
> - Add platform-specific `autoComplete` (`android: 'sms-otp'`, default:
`'one-time-code'`) and type `TextInputProps`.
> - Cast `CodeField` refs to `React.RefObject<TextInput>`.
> - **Resend/cooldown behavior**
> - In `ConfirmPhoneNumber`, set cooldown after successful resend;
maintain guard against rapid presses with `resendInProgressRef`.
> - In `CardAuthentication`/`ConfirmEmail`, ensure countdown timers and
resend handlers respect cooldown state.
> - **Tests**
> - Update `react-native-confirmation-code-field` mocks to include
`useBlurOnFulfill` in related test files.
> - Minor adjustments (e.g., variable rename for
`contactVerificationId`) to align with implementation.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
158a841. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Change tracked to enrolled and related strings
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
Changing Rewards strings to better terminology "enrolled" / "not
enrolled"
## **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: 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**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Updates Rewards i18n strings in `locales/languages/en.json` to replace
“opt-in/tracked” with “enrolled” and adjusts related labels/descriptions
and error text.
>
> - **Rewards i18n (`locales/languages/en.json`)**:
> - Terminology: replace “opt-in/opted in/tracked” with
“enroll/enrolled”.
> - `onboarding.not_supported_account_type_description`: “opted into” →
“enrolled in”.
> - `onboarding.geo_check_fail_description`: “country allows opting
into” → “region allows enrolling into”.
> - `settings.*`: maintain “opted in” counts but update related copy;
error `link_account_failed_error`: “link” → “add”.
> - `link_account_group.tracked/untracked/unsupported/tracked_count`:
“Tracked/Untracked/Unsupported” → “Enrolled/Not enrolled/Not supported”;
count string updated to “enrolled”.
> - Misc: `link_account_failed_error` under settings changed to “Failed
to add account.”
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d194816. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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 : )