Skip to content

[pull] main from MetaMask:main#302

Merged
pull[bot] merged 13 commits into
Reality2byte:mainfrom
MetaMask:main
Nov 6, 2025
Merged

[pull] main from MetaMask:main#302
pull[bot] merged 13 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Nov 6, 2025

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

GeorgeGkas and others added 13 commits November 6, 2025 12:52
<!--
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**

- Reduce padding in network fee item when fee is included
- Fix padding issues of the selected token row in Select Token modal.
- Fix issues with some native token images rendered with bigger size in
Select Token modal.
- Apply the correct navigation title style.

<!--
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: Various UI fixes on swap component.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-3337

## **Manual testing steps**

```gherkin
Validate that the correct styles are applied and rendered based on the provided screenshots.
```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**
<img width="430" height="51" alt="Screenshot 2025-10-30 at 6 58 44 PM"
src="https://github.com/user-attachments/assets/ecdf0f18-dca2-4af3-83bc-d460d1db61c5"
/>

<img width="430" height="93" alt="Screenshot 2025-10-30 at 6 59 47 PM"
src="https://github.com/user-attachments/assets/9f333e80-cee2-414b-9708-5c1ae3735af3"
/>

<img width="430" height="277" alt="Screenshot 2025-10-30 at 7 02 27 PM"
src="https://github.com/user-attachments/assets/fb8c2ab2-55d6-416e-9ef5-a81296defcfa"
/>


<!-- [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]
> Refines Bridge UI typography/padding, standardizes label casing,
adjusts native token icon sizes, and adds a toSentenceCase util with
tests and e2e updates.
> 
> - **Bridge UI/Styling**:
> - `TokenButton`: switch symbol text to `HeadingLG`, use themed size
and `fontWeight: 500`.
> - Token symbol text in `BridgeView` snapshots updated (`lineHeight`
32, `fontWeight` 500).
> - **Token Selectors**:
> - `TokenSelectorItem`: fix selected row alignment
(`selectedItemWrapperReset`), constrain native token icon to `32x32`,
keep badge wrapper alignment.
> - Snapshot updates for source/dest selectors reflecting `{}` style,
icon sizing, and layout tweaks.
> - **Quote Details**:
> - Use `toSentenceCase` for labels (`network_fee`, `minimum_received`,
`price_impact`, `points`).
> - Slippage row: move edit icon before value and change icon color to
`Alternative`; tighten spacing in included network fee row (`gap` 2).
> - **Tooltip & Navbar**:
> - `TooltipModal`: reduce horizontal padding and change title variant
to `HeadingSM`.
>   - `NavbarTitle`: change title variant to `HeadingSM`.
> - **Utils/Tests/E2E**:
> - Add `toSentenceCase` in `app/util/string` with comprehensive tests.
>   - Update e2e selectors to use sentence-cased `NETWORK_FEE`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
890fe0e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…2246)

## **Description**

This PR refactors Perps selectors and utility functions for better
performance and code quality:

**Reason for change:**
1. The `selectTradeConfiguration` selector was creating new object
references on every call, causing unnecessary re-renders and breaking
React dependency tracking in `useMemo`/`useEffect`
2. Manual string matching for EVM account type checking
(`account.type.startsWith('eip155:')`) was fragile and not using
official MetaMask utilities

**Solution:**
1. Converted `selectTradeConfiguration` to use `createSelector` from
reselect for memoization, ensuring stable object references
2. Replaced manual string matching with official `isEvmAccountType`
utility from `@metamask/keyring-api`

**Impact:**
- Eliminates unnecessary component re-renders
- Better React dependency tracking
- More maintainable code using official utilities
- Added null safety checks

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: TAT-2025

## **Manual testing steps**

```gherkin
Feature: Perps selector and utility refactoring

  Scenario: verify no behavioral changes
    Given the Perps feature is accessible

    When user accesses any Perps functionality (order placement, watchlist, configuration)
    Then all functionality works identically to before
    And no visual changes are observed
    And no errors appear in console
```

**Automated Testing:**
All existing tests pass without modification:
- � Selector tests: 19/19 passed
- � Account utils tests: 5/5 passed
- � Wallet service tests: 24/24 passed

## **Screenshots/Recordings**

### **Before**
N/A - Internal refactoring only, no UI changes

### **After**
N/A - Internal refactoring only, no UI changes

## **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 (all existing tests pass)
- [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.

---

## **Technical Details**

### Files Changed (4 files):

1. **app/components/UI/Perps/controllers/selectors.ts**
   - Added `createSelector` import from reselect
- Converted `selectTradeConfiguration` to memoized selector with 3 input
selectors
- Returns stable object references: `{ leverage?: number } | undefined`

2. **app/components/UI/Perps/controllers/selectors.test.ts**
   - No changes needed (tests already compatible)

3. **app/components/UI/Perps/utils/accountUtils.ts**
   - Added `isEvmAccountType` import from `@metamask/keyring-api`
- Replaced `account.type.startsWith('eip155:')` with
`isEvmAccountType(account.type)`
   - Added null safety: `account && isEvmAccountType(account.type)`

4. **app/components/UI/Perps/services/HyperLiquidWalletService.test.ts**
   - Added mock for `@metamask/keyring-api` to prevent import errors
   - Mock matches existing test data (`'eip155:1'`)

### Risk Assessment
=� **Very Low Risk**
- Pure refactoring with zero behavioral changes
- All existing tests pass without modification
- No API contract changes
- Comprehensive test coverage

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Memoizes perps trade config selector and switches EVM account
detection to keyring-api utility, with tests updated to mock the new
import.
> 
> - **Controllers (`controllers/selectors.ts`)**:
> - Refactor `selectTradeConfiguration` to a memoized `createSelector`,
returning stable `{ leverage } | undefined` per network/coin.
> - **Utils (`utils/accountUtils.ts`)**:
> - Replace string prefix check with `isEvmAccountType` for EVM account
detection, with null-safety.
> - **Tests (`services/HyperLiquidWalletService.test.ts`)**:
> - Add mock for `@metamask/keyring-api` (`isEvmAccountType`) to prevent
import issues.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
dbd08fe. 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 removes the use of `selectMultichainAccountsState2Enabled` from
the BIP-44 intro modal.

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

## **Manual testing steps**

N/A

## **Screenshots/Recordings**

N/A

## **Pre-merge author checklist**

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

## **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]
> Removes the feature-flag dependency from the Multichain Accounts intro
modal logic and updates tests to rely only on seen state and version
update boundaries.
> 
> - **Hook
(`app/components/hooks/useMultichainAccountsIntroModal.ts`)**:
> - Remove `selectMultichainAccountsState2Enabled` usage, related
condition, and dependency.
> - Modal now shows only when `!hasSeenIntroModal` and app updates
across the 7.56 → 7.57 boundary.
> - Drop `isMultichainAccountsState2Enabled` from returned state and
dependencies.
> - **Tests
(`app/components/hooks/useMultichainAccountsIntroModal.test.ts`)**:
> - Remove feature-flag setup/assertions and the "feature disabled"
case.
> - Update initial state and expectations to cover only seen-state and
version-boundary scenarios.
> - Keep navigation assertions and version matrix validating boundary
logic.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4807ed3. 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**
Implement useRampNavigation hook that navigates the user to the
appropriate ramp flow.
This hook exports a goToRamps method that can be used in every endpoint
to navigate to either Aggregator or Deposit accordingly.

<!--
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:
https://consensyssoftware.atlassian.net/browse/TRAM-2814?atlOrigin=eyJpIjoiNGUwNjI3YTJkYzQ2NGM5YzkxYzI5YWJkN2ZhZjliZTciLCJwIjoiaiJ9

## **Manual testing steps**

N/A

## **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]
> Adds `useRampNavigation` to navigate to aggregator (buy/sell) or
deposit flows, with early return when Unified V1 is enabled, plus unit
tests.
> 
> - **Hooks**:
> - Introduces `useRampNavigation` providing `goToRamps` to route based
on `RampMode`.
> - Routes to `createBuyNavigationDetails` or
`createSellNavigationDetails` for `AGGREGATOR` (defaulting to `BUY`),
passing optional `intent`.
> - Routes to `createDepositNavigationDetails` for `DEPOSIT`, passing
params.
> - Early-returns (no navigation) when `useRampsUnifiedV1Enabled` is
true.
> - **Tests**:
> - Adds `useRampNavigation.test.ts` covering default and explicit
`BUY`/`SELL`, intent/params forwarding, `DEPOSIT` flow, and Unified V1
early-return behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
caddf25. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Before this PR, the silent auth function was calling the get opt in
status endpoint (under some circumstances) for an individual address.
This is fine if it's called in some places, but not so efficient when
it's called for an entire account group via an auth trigger (i.e.
account group changed). In this new implementation, we now do this in
bulk before attempting to start calling silent auth for any account of
the account group.

We're also adding a new defined exception for when the season state
returns a 404. In this case we also clear some of the state in the app.

## **Changelog**

CHANGELOG entry: null

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

- [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]
> Prefetches bulk opt-in status for account groups, makes silent-auth
skip token-aware, and throws/handles SeasonNotFound by clearing season
cache.
> 
> - **RewardsController**:
> - Prefetch `getOptInStatus` for sorted accounts before running
per-account silent auth.
> - Make `shouldSkipSilentAuth` async and require valid session token
when `hasOptedIn` is true.
>   - Await async skip check in `performSilentAuth`.
> - On `SeasonNotFoundError` in `getSeasonStatus`, clear `state.seasons`
and rethrow.
> - **RewardsDataService**:
> - Add `SeasonNotFoundError`; throw when 404 message indicates season
not found in `getSeasonStatus`.
> - **Tests**:
> - Update tests to await async `shouldSkipSilentAuth`, validate token
checks, and add coverage for `SeasonNotFoundError` handling.
> - Adjust mocks to support new call flow (bulk opt-in status, token
retrieval).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fdd6b1a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…22048)

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

Fix an issue where on Solana blockchain swaps, upon navigating to
transaction screen, users where informed that the swap lives in two
blockchains, while it should redirect them to Solscan upon clicking
"View on Block Explorer".

<!--
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: Fix issue where Solana swaps incorrectly displayed as
cross-chain; ensure “View on Block Explorer” redirects to Solscan.

## **Related issues**

Fixes: #18136

## **Manual testing steps**

```gherkin
Feature: Same-chain Solana swap transaction details

  Scenario: user views block explorer for a same-chain Solana swap transaction
  
    Given the user has completed a same-chain swap on Solana (SOL to USDC)
    And the transaction is visible in the activity tab
    
    When user navigates to the transaction details
    And user clicks "View on Block Explorer" button
    
    Then the app opens an integrated browser tab with the Solscan transaction URL
    And the cross-chain bridge modal is NOT displayed
```

## **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 source tx hash to open the block explorer directly for same-chain
swaps, including Solana, and add tests/mocks to cover this behavior.
> 
> - **Bridge Transaction Details**:
> - Use `bridgeTxHistoryItem.status.srcChain.txHash` (fallback to
`evmTxMeta.hash`) to build explorer URL for swaps via
`useMultichainBlockExplorerTxUrl`.
> - "View on Block Explorer" now navigates directly to browser for
swaps; retains modal for bridges.
> - **Tests**:
> - Add test ensuring same-chain Solana swap opens browser with URL
containing the Solana tx hash.
> - Update mocks and imports; rename `mockTx` to `mockEVMTx` and add
`mockMultiChainTx`.
> - **Mocks/State**:
> - Extend `_mocks_/initialState.ts` with a `solana-swap-tx` entry to
simulate a Solana same-chain swap.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3dd3656. 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**

Fixes a discrepancy where the funding rate shown in the Perps markets
list differed from the asset detail screen. The detail screen used live
WebSocket data and `formatFundingRate()`, while the list used static
data and manual formatting.

## **Changelog**

CHANGELOG entry: Fixed funding rate display inconsistency between Perps
markets list and asset detail screen

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-1975

## **Manual testing steps**

```gherkin
Feature: Perps funding rate display consistency

  Scenario: funding rate matches between markets list and detail screen
    Given user is on the Perps markets list view
    And user has selected "Funding Rate" as the display metric
    When user views the funding rate for any market in the list
    Then the funding rate displayed matches the funding rate shown on the asset detail screen
    And the funding rate updates in real-time from WebSocket data
```

## **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/11a1cf39-8077-4707-8498-db74f2391e68




### **After**


https://github.com/user-attachments/assets/d5e10cf2-7bb8-404f-bf3c-286b1fccac47


<!-- [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]
> Displays funding rate from live WebSocket data (even when price is
unchanged) and formats it via formatFundingRate; adds comprehensive
tests.
> 
> - **PerpsMarketRowItem** (`PerpsMarketRowItem.tsx`):
> - **Live data merge**: Update `market.fundingRate` from
`livePrice.funding` and trigger updates even if `price` is unchanged.
> - **Formatting**: Use `formatFundingRate` for
`displayMetric="fundingRate"` for consistent 4-decimal percentage
output.
> - **Tests** (`PerpsMarketRowItem.test.tsx`):
> - Add funding rate test suite covering positive/negative/zero,
undefined/null, very small/large values, live updates, unchanged values,
and `onPress` propagation of updated data.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e6329c6. 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**

Tron accounts controlled by feature flags

<!--
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: chore: Tron accounts controlled by feature flags

## **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 Tron accounts feature-flag gating with provider wrapper, runtime
toggling and wallet alignment; introduces selector/tests; includes Tron
in Metro features; bumps iOS bundle size threshold.
> 
> - **Core (multichain account service)**
> - Wrap `TrxAccountProvider` with `AccountProviderWrapper` and
initialize via `isTronAccountsFeatureEnabled`.
> - Align wallets if any of `bitcoin`/`tron` is enabled initially;
subscribe to flag changes to toggle providers and align when enabled.
> - **Feature flags**
> - Add `app/multichain-tron/remote-feature-flag.ts` with
`isTronAccountsFeatureEnabled`.
>   - New selector `selectIsTronAccountsEnabled` with unit tests.
> - **Tests**
> - Extend `multichain-account-service-init.test.ts` for Tron flag
scenarios; simplify Bitcoin assertions and reset spies between events.
> - **Build/CI**
>   - Include `tron` in Metro main/beta feature sets.
>   - Increase iOS JS bundle size threshold to `52`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
acba6f9. 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 addresses several maintainability issues reported by SonarCloud:
🔗 [View related issues on
SonarCloud](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=22058&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)

Additionally, it reverts a temporary workaround previously added for
Android to prevent a crash occurring when users navigated through the
KYC WebView.
The root cause of the WebView crash has been resolved in a separate PR,
making the temporary fix unnecessary.

## **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: Reverted temporary workaround for KYC WebView crash
(issue resolved in upstream fix).

## **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]
> Refactors Card features: rewrites asset selection and balance logic,
unifies KYC verification navigation to WebView, tightens onboarding
routing, improves SDK/logging, and adds extensive tests.
> 
> - **Card Home**:
> - Add `enableCardAction` tests covering success/failure/loading paths;
mock `Logger.log`.
>   - Log errors in `enableCardAction` catch.
> - **Asset Selection Bottom Sheet**:
> - Major refactor with helper utilities (network filtering, token
mapping/deduping, CAIP handling, sorting by priority/allowance).
> - Integrate balances via `useAssetBalances`; show allowance state
text; improve list rendering and Solana footer.
> - Preserve existing navigation and priority update, and allow Limited
tokens to update priority directly.
> - **Balances (`useAssetBalances`)**:
> - Extract helpers; improve fiat calculation (market data, Solana
rates, proportional fallback), normalization, and asset building.
> - Update tests with extensive scenarios (enabled/limited/not-enabled,
fallbacks, edge cases).
> - **Onboarding/KYC**:
> - Verify Identity: always navigate to WebView; simplify tests; add
screen view metric.
> - OnboardingNavigator: require `firstName` and `countryOfNationality`
before address; update tests.
>   - Types: add `countryOfNationality` to `UserResponse`.
> - **SDK**:
> - Improve error logging/handling (external wallet details, provision
card); simplify network-to-CAIP mapping; minor cleanup.
> - **Priority/External Wallet Hooks**:
> - Extract `determineAllowanceState`; use nullish coalescing where
applicable; add tests for warnings and fetching behaviors.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
94c9b08. 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 aims to fix BTC amount validation in send flow. 

As we will implement `onAmountInput` validations - this is a temporary
fix to handle it in the client code. In near future this validation will
be handled in snap repository.

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

## **Manual testing steps**

1. Go send flow
2. Pick BTC
3. Try sending lower than 0.0001
4. You shouldn't be able to proceed to recipient page

## **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]
> Enforces a 0.0001 BTC minimum in send amount validation and adds tests
for BTC thresholds.
> 
> - **Validation**:
> - Update `useAmountValidation` to use `useSendType` and `bignumber.js`
to enforce a Bitcoin minimum amount of `0.0001`.
> - Add temporary Bitcoin-specific check returning
`strings('send.invalid_value')` when below threshold.
>   - Update memo dependencies to include `isBitcoinSendType`.
> - **Tests**:
> - Extend `useAmountValidation.test.ts` to mock `useSendType` and cover
BTC amounts below, at, and above the minimum threshold.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ab9acac. 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?
-->
Some small changes:
- Fix eslint warnings
- Remove unnecessary comments
- Refactor state update for predict balance

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

## **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]
> Fixes ESLint warnings across hooks/views, refactors `getBalance` cache
update, and streamlines tests/mocks (including unskipping market data
test).
> 
> - **PredictController**:
> - Refactor `getBalance` cache update to safely initialize
`state.balances[providerId]` before setting `address` entry.
> - **Hooks**:
> - Add targeted ESLint-disable lines for React hook deps in
`usePredictBalance`, `usePredictPositions`, `usePredictPriceHistory`,
`useUnrealizedPnL`.
> - Views (`PredictBuyPreview`, `PredictSellPreview`): add
ESLint-disable on empty deps effects.
> - **Tests**:
>   - Unskip and run `usePredictMarketData` success test.
> - Clean up withdraw tests by removing obsolete eligibility comments;
keep behavior intact.
> - Normalize mock naming (`MockedEngine`, `MockedPredictActivityType`)
and minor mock adjustments in transactions/activity tests.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7125c5f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ay (#22248)

## **Description**

This PR improves the user experience of the Perps trading interface by
adding skeleton placeholder loading states and fixing the rewards points
display to prevent "undefined" from appearing in the UI.

### What is the reason for the change?

**Problem 1: No Loading Feedback**
- Users experienced no visual feedback during long-running operations
(amount optimization, fee calculation)
- This created perception of slow/broken UI and risk of
double-submission
- Blank spaces appeared during data loading

**Problem 2: Rewards Display Showing "undefined"**
- Rewards points row was displaying even when `estimatedPoints` was
`undefined`
- "undefined" text was visible in production
- Only checked `shouldShowRewardsRow` flag, not the actual data value

### What is the improvement/solution?

**Loading States:**
- Added `isLoading` prop to `PerpsAmountDisplay` component
- Implemented skeleton placeholder (80x20px) during amount optimization
- Fee display now shows "--" during calculation instead of blank space

**Rewards Fix:**
- Added compound condition: `shouldShowRewardsRow && estimatedPoints !==
undefined`
- Rewards row only appears when data is valid
- Eliminates "undefined" text from appearing in UI

## **Changelog**

CHANGELOG entry: Added skeleton loading states for amount optimization
and fee calculation, fixed rewards points display to prevent undefined
values

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-2024

## **Manual testing steps**

```gherkin
Feature: Perps Order Loading States

  Scenario: user enters order amount
    Given user is on the Perps order view
    And the order form is empty

    When user enters an amount and closes the keypad
    Then a skeleton placeholder should appear during amount optimization
    And the fee display should show "--" during calculation
    And no blank spaces should appear

  Scenario: user views rewards points
    Given user is on the Perps order view
    And user has entered a valid order amount

    When rewards calculation completes successfully
    Then rewards points row should appear with valid point value

    When rewards calculation fails or returns undefined
    Then rewards points row should not appear
    And "undefined" text should never be visible
```

## **Screenshots/Recordings**

### **Before**

- No loading feedback during optimization (blank display)
- Fee calculation showed blank space
- Rewards row showed "undefined" when data was unavailable
- Users clicking multiple times due to lack of feedback

### **After**

- Skeleton placeholder appears during amount optimization (professional
loading UX)
- Fee display shows "--" during calculation (clear loading state)
- Rewards row only appears when points data is valid (no "undefined"
text)
- Clear visual feedback prevents user confusion

## **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 (UI changes, existing tests
cover functionality)
- [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.

---

## **Technical Details**

### Files Modified

1. **`PerpsAmountDisplay.tsx`**
   - Added `isLoading?: boolean` prop to interface
- Imported `SkeletonPlaceholder` from
'react-native-skeleton-placeholder'
- Implemented conditional skeleton rendering (80x20px, 4px border
radius)

2. **`PerpsOrderView.tsx`**
- Passed `isLoading={!hasValidAmount ||
feeResults.isLoadingMetamaskFee}` to PerpsAmountDisplay
   - Updated fee display to check loading state and show fallback
   - Fixed rewards display condition to check both flag AND data value

### Risk Level
=� **Low Risk** - UI-only changes, no business logic modifications

### Validation
- � ESLint validation passed
- � TypeScript validation passed
- � All changes follow existing patterns in codebase

### Benefits
- Clear feedback during loading (no user confusion)
- Professional UX (standard loading pattern)
- Improved perceived performance
- Eliminates "undefined" text in UI
- Clean rewards display (only when valid)
- Prevents double-submission attempts

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds amount skeleton loading, shows fallback fees while
loading/invalid, and hides rewards row unless points are defined.
> 
> - **Perps UI**:
> - **Amount**: `PerpsAmountDisplay` gains `isLoading` prop and skeleton
placeholder; refactors display value calculation.
> - **Fees**: In `PerpsOrderView`, `PerpsFeesDisplay` now shows
`PERPS_CONSTANTS.FALLBACK_DATA_DISPLAY` when amount is invalid or
`isLoadingMetamaskFee`, otherwise formats fees.
> - **Rewards**: Rewards row renders only if `shouldShowRewardsRow` AND
`estimatedPoints !== undefined`.
> - **Wiring**: `PerpsOrderView` passes `isLoading={isLoadingAccount}`
to `PerpsAmountDisplay`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
25ac425. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
# Geoblock Analytics Implementation

## Overview

This PR implements analytics tracking for geoblocking events in Predict,
allowing us to understand when and where users are blocked from
performing actions due to geographic restrictions.

CHANGELOG entry: null

## Changes

### 1. State Structure Refactoring

**Consolidated eligibility and geoblock data into a single nested
structure:**

```typescript
// Before
eligibility: { [key: string]: boolean }
geoBlockData: { [key: string]: { country?: string } }

// After
eligibility: {
  [key: string]: {
    eligible: boolean;
    country?: string;
  }
}
```

**Benefits:**
- Simplified state management - one source of truth
- Atomic updates - eligible status and country always in sync
- Easier to consume - no need to access two separate state objects

### 2. Analytics Event Implementation

**Event:** `PREDICT_GEO_BLOCKED_TRIGGERED`

**Properties:**
- `country` - The country code where the user is located (from geoblock
API)
- `attempted_action` - The action the user tried to perform

**Attempted Actions:**
- `deposit` - User tried to add funds
- `predict_action` - User tried to place a prediction
- `cashout` - User tried to cash out a position
- `claim` - User tried to claim winnings
- `withdraw` - User tried to withdraw funds (not currently guarded)

### 3. Integration Points

**Guarded Actions:**
All actions that require eligibility checks use `executeGuardedAction`
from `usePredictActionGuard` hook:

```typescript
executeGuardedAction(
  () => {
    // Action logic
  },
  { attemptedAction: PredictEventValues.ATTEMPTED_ACTION.DEPOSIT }
);
```

**Components Updated:**
- ✅ `PredictBalance.tsx` - Add funds (deposit)
- ✅ `PredictAddFundsSheet.tsx` - Add funds (deposit)
- ✅ `PredictMarketDetails.tsx` - Buy (predict), Claim
- ✅ `PredictPositionsHeader.tsx` - Claim
- ✅ `PredictPositionDetail.tsx` - Cash out
- ✅ `PredictMarketSingle.tsx` - Buy (predict)
- ✅ `PredictMarketOutcome.tsx` - Buy (predict)
- ✅ `PredictMarketMultiple.tsx` - Buy (predict)

### 4. Controller Updates

**`PredictController.ts`:**
- `refreshEligibility()` - Now stores both `eligible` and `country`
together
- `trackGeoBlockedEvent()` - New method to track analytics when user is
blocked
- Updated to use consolidated eligibility state

**`usePredictEligibility.ts` hook:**
- Now returns both `isEligible` and `country`
- Simplifies component consumption

### 5. Testing

**All tests updated and passing:**
- ✅ PredictController tests (1687 tests)
- ✅ usePredictEligibility tests
- ✅ Component tests updated for new hook signature
- ✅ Test suites: 67 passed

## Data Flow

```
User Action Attempt
       ↓
executeGuardedAction
       ↓
Check eligibility (from state)
       ↓
   Blocked? → Yes → trackGeoBlockedEvent(country, attemptedAction)
       ↓                        ↓
       No              Analytics Event Sent
       ↓
Execute Action
```

## Analytics Dashboard

**To query geoblock events in the analytics dashboard:**

```sql
SELECT 
  country,
  attempted_action,
  COUNT(*) as blocked_count,
  DATE(timestamp) as date
FROM events
WHERE event_name = 'PREDICT_GEO_BLOCKED_TRIGGERED'
GROUP BY country, attempted_action, DATE(timestamp)
ORDER BY blocked_count DESC
```

**Key Metrics:**
- Most blocked countries
- Most common blocked actions
- Trends over time
- Conversion rates by geography

## Example Event

```json
{
  "event": "PREDICT_GEO_BLOCKED_TRIGGERED",
  "properties": {
    "country": "US",
    "attempted_action": "deposit"
  },
  "timestamp": "2024-01-15T10:30:00.000Z"
}
```

## Files Changed

### Core Implementation
- `app/components/UI/Predict/controllers/PredictController.ts`
- `app/components/UI/Predict/hooks/usePredictEligibility.ts`
- `app/components/UI/Predict/hooks/usePredictActionGuard.ts`
- `app/components/UI/Predict/constants/eventNames.ts`

### Components
-
`app/components/UI/Predict/components/PredictBalance/PredictBalance.tsx`
-
`app/components/UI/Predict/components/PredictAddFundsSheet/PredictAddFundsSheet.tsx`
-
`app/components/UI/Predict/views/PredictMarketDetails/PredictMarketDetails.tsx`
-
`app/components/UI/Predict/components/PredictPositionsHeader/PredictPositionsHeader.tsx`
-
`app/components/UI/Predict/components/PredictPositionDetail/PredictPositionDetail.tsx`
-
`app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.tsx`
-
`app/components/UI/Predict/components/PredictMarketOutcome/PredictMarketOutcome.tsx`
-
`app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.tsx`

### Tests
- `app/components/UI/Predict/controllers/PredictController.test.ts`
- `app/components/UI/Predict/hooks/usePredictEligibility.test.ts`
-
`app/components/UI/Predict/components/PredictAddFundsSheet/PredictAddFundsSheet.test.tsx`

## Testing Instructions

### Manual Testing

1. **Setup geoblock scenario:**
- Use VPN or modify geoblock API response to return `isEligible: false`

2. **Test each action:**
   - Try to deposit funds → Check analytics event sent
   - Try to place a prediction → Check analytics event sent
   - Try to cash out → Check analytics event sent
   - Try to claim winnings → Check analytics event sent

3. **Verify analytics:**
   - Open DevTools console
   - Look for `📊 [Analytics] PREDICT_GEO_BLOCKED_TRIGGERED` logs
   - Verify `country` and `attempted_action` are correct

### Automated Testing

```bash
# Run all Predict tests
yarn jest ./app/components/UI/Predict

# Run specific test suites
yarn jest app/components/UI/Predict/controllers/PredictController.test.ts
yarn jest app/components/UI/Predict/hooks/usePredictEligibility.test.ts
```

## Migration Notes

### State Migration

No migration required. The consolidated state structure is backward
compatible:
- Old state with empty `eligibility` object will work correctly
- `refreshEligibility()` will populate new structure on first call

### API Compatibility

No API changes required. The geoblock API response format remains the
same:
```typescript
interface GeoBlockResponse {
  isEligible: boolean;
  country?: string;
}
```

## Future Enhancements

1. **Enhanced Metrics:**
   - Add `ip` to analytics (currently stored but not tracked)
   - Track `region` for more granular insights

2. **User Education:**
   - Show country-specific messaging
   - Suggest alternative actions for blocked users

3. **Dashboard Integration:**
   - Real-time geoblock monitoring
   - Alerts for sudden spikes in blocked regions

## Checklist

- [x] State structure refactored and tested
- [x] Analytics event implemented
- [x] All guarded actions updated with `attemptedAction`
- [x] Tests updated and passing (67 suites, 1687 tests)
- [x] Dev logging added for debugging
- [x] Code follows project guidelines
- [x] No breaking changes

## Related Issues

- Tracking user geoblocking for compliance and UX insights
- Understanding geographic distribution of blocked actions


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add geo-block analytics and migrate eligibility to include country,
wiring attemptedAction tracking across guarded Predict actions.
> 
> - **Analytics**:
> - Add `PREDICT_GEO_BLOCKED_TRIGGERED` event and properties `country`
and `attempted_action`.
> - **State/Controller**:
> - Change `PredictController.state.eligibility` to `{ [providerId]: {
eligible: boolean; country?: string } }`.
> - Update `refreshEligibility()` to store country; add
`trackGeoBlockTriggered()`.
> - **Providers**:
> - Update `PredictProvider.isEligible()` to return `{ isEligible,
country }` via `GeoBlockResponse`.
>   - Implement in `PolymarketProvider.isEligible()`.
> - **Hooks**:
> - `usePredictEligibility()` returns `{ isEligible, country }` (default
`false` when unset).
> - `usePredictActionGuard()` accepts `{ checkBalance, attemptedAction
}` and tracks geo-blocks when blocked.
> - **UI (guarded actions instrumented with `attemptedAction`)**:
>   - `PredictAddFundsSheet`, `PredictBalance` → deposit.
> - `PredictMarketDetails`, `PredictMarketSingle`,
`PredictMarketOutcome`, `PredictMarketMultiple` → predict/buy, claim.
> - `PredictPositionsHeader` → claim; `PredictPositionDetail` → cashout.
> - **MetaMetrics**:
>   - Register new event in `MetaMetrics.events`.
> - **Tests**:
> - Update controller, hooks, provider, and component tests for new
eligibility shape, geo-block tracking, and attemptedAction args.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e6d0566. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators Nov 6, 2025
@pull pull Bot added the ⤵️ pull label Nov 6, 2025
@pull pull Bot merged commit 5b5c76c into Reality2byte:main Nov 6, 2025
4 of 37 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.