Skip to content

[pull] main from MetaMask:main#589

Merged
pull[bot] merged 3 commits intoReality2byte:mainfrom
MetaMask:main
Mar 10, 2026
Merged

[pull] main from MetaMask:main#589
pull[bot] merged 3 commits intoReality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Mar 10, 2026

See Commits and Changes for more details.


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

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

weitingsun and others added 3 commits March 9, 2026 23:47
<!--
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**
Start to use builds.yml locally.

If we have `BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY` set to true
locally in .js.env then we will use the envs in Builds.yml and .js.env.

Here are the steps: 
1. Source .js.env (early, for the gate).
2. If flag BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY is true: run
loadBuildConfig → applies builds.yml (overwrites env).
3. Source .js.env again → “Local builds: .js.env overrides builds.yml” —
so .js.env wins over anything set by builds.yml.
4. Legacy remap runs (Bitrise-style vars).

This way - when we retire BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY, all
the local builds will use builds.yml and .js.env
 
## **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: Added ability for dev build to use builds.yml as source
of truth

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Build configuration for local builds
  As a local developer
  I want build configuration to load from .js.env or builds.yml depending on a flag
  So that I can use builds.yml when I opt in and .js.env always overrides when present

  Scenario: Local without flag uses .js.env only
    Given I am running the build script locally
    And .js.env exists
    And BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY is not set to "true"
    When I run the build script
    Then loadBuildConfig should not run
    And .js.env should be sourced
    And build env should come from .js.env

  Scenario: Local with flag true uses builds.yml then .js.env overrides
    Given I am running the build script locally
    And .js.env exists and sets BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY=true
    When I run the build script
    Then loadBuildConfig should run and load from builds.yml
    And .js.env should be sourced again after that
    And values from .js.env should override values from builds.yml

  Scenario Outline: .js.env overrides builds.yml for same variable
    Given I am running the build script locally with BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY=true
    And builds.yml sets <var> to "<builds_value>"
    And .js.env sets <var> to "<env_value>"
    When I run the build script
    Then the effective <var> should be "<env_value>"

    Examples:
      | var              | builds_value                         | env_value                               |
      | REWARDS_API_URL  | https://rewards.api.cx.metamask.io   | https://rewards.uat-api.cx.metamask.io |
      | MM_PORTFOLIO_URL | https://portfolio.api.cx.metamask.io | https://portfolio.dev-api.cx.metamask.io |

```

## **Screenshots/Recordings**

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

### **Before**

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

### **After**
Using Pre build
<img width="200" height="400" alt="Simulator Screenshot - iPhone 16 -
2026-03-05 at 12 55 12"
src="https://github.com/user-attachments/assets/65815b46-b205-4071-ba74-d8ffee7a163f"
/>

Using local build (yarn start:ios)
<img width="200" height="400" alt="Simulator Screenshot - iPhone 16 -
2026-03-05 at 15 08 27"
src="https://github.com/user-attachments/assets/4c25e3ca-b839-4e54-86f7-ded1b383c74f"
/>

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

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches the build pipeline and environment variable precedence, which
can break local/CI builds if the gating or sourcing order is wrong.
Runtime app changes are limited to extra debug info on the settings
screen.
> 
> **Overview**
> **Build configuration changes:** `scripts/build.sh` now conditionally
loads `builds.yml` when `BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY=true`
(for GitHub Actions and local opt-in), sources `.js.env` early to read
the flag, then sources it again afterward so local values override
`builds.yml`. Legacy Bitrise env remapping is kept for non-GHA runs, and
missing `*_DEV/_QA/_PROD` secrets only hard-fail on the legacy path.
> 
> **Debug visibility:** The hidden environment info section in
`AppInformation` now also displays `REWARDS_API_URL` and
`MM_PORTFOLIO_URL` (or `—` if unset).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
54fe502. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Register the new `@metamask/geolocation-controller` in the mobile Engine
and migrate all 4 existing geolocation consumers (Ramp, Perps, Rewards,
Card) to use the centralised controller instead of their individual
implementations.

### Problem

Four separate features independently fetch geolocation from the same
API, each with their own URL constants, caching logic, and error
handling:

- **Ramp** — `useDetectGeolocation` hook with `GEOLOCATION_URLS`
constant
- **Perps** — `EligibilityService.fetchGeoLocation()` with
`ON_RAMP_GEO_BLOCKING_URLS`, TTL caching, and promise deduplication
- **Rewards** — `RewardsDataService.fetchGeoLocation()` with
`GEOLOCATION_URLS` constant
- **Card** — `CardSDK.getGeoLocation()` with a hardcoded URL

This results in 2–4 redundant API calls on app load and duplicated code
across the codebase.

### Solution

The core `@metamask/geolocation-controller` package provides two
components:

- **`GeolocationApiService`** — handles HTTP fetch, TTL caching (5 min),
and promise deduplication
- **`GeolocationController`** — manages UI-facing state (`location`,
`status`, `lastFetchedAt`, `error`) and delegates to the ApiService via
messenger

This PR:

1. **Registers both components** in the Engine following the established
`RewardsController`/`RewardsDataService` pattern
2. **Eagerly fetches** geolocation on Engine start so the value is
available before any consumer needs it
3. **Migrates all 4 consumers** to read from the centralised controller:
- **Ramp**: Deleted `useDetectGeolocation` hook; redefined
`getDetectedGeolocation` selector to read from `GeolocationController`
state (preserves the 10+ downstream consumer files unchanged)
- **Perps**: `refreshEligibility()` calls
`GeolocationController:getGeolocation` via messenger and passes the
result to `checkEligibility()` as a parameter; removed all
geolocation-fetching code from `EligibilityService`
- **Rewards**: Replaced `RewardsDataService:fetchGeoLocation` messenger
call with `GeolocationController:getGeolocation` in
`RewardsController.getGeoRewardsMetadata()`; removed
`fetchGeoLocation()` method from `RewardsDataService`
- **Card**: Replaced `cardSDK.getGeoLocation()` with
`Engine.context.GeolocationController?.state?.location` in
`getCardholder.ts`; removed `getGeoLocation()` from `CardSDK`
4. **Removes all duplicate** `GEOLOCATION_URLS`,
`ON_RAMP_GEO_BLOCKING_URLS`, and hardcoded geolocation URL constants

### Files changed summary

**Created (6 files):**
| File | Purpose |
|------|---------|
| `app/core/Engine/controllers/geolocation-api-service-init.ts` | Init
function with environment-aware `SDK.Env` mapping |
| `app/core/Engine/messengers/geolocation-api-service-messenger.ts` |
Simple messenger factory (no delegated actions) |
| `app/core/Engine/controllers/geolocation-controller/index.ts` | Init
function with eager geolocation fetch |
| `app/core/Engine/messengers/geolocation-controller-messenger/index.ts`
| Messenger delegating `GeolocationApiService:fetchGeolocation` |
| `app/selectors/geolocationController/index.ts` | Redux selectors for
controller state |
| `app/core/Engine/controllers/geolocation-api-service-init.test.ts` |
Unit tests for env mapping |

**Created (test, 1 file):**
| File | Purpose |
|------|---------|
| `app/core/Engine/controllers/geolocation-controller/index.test.ts` |
Unit tests for init + eager fetch |

**Deleted (2 files):**
| File | Reason |
|------|--------|
| `app/components/UI/Ramp/hooks/useDetectGeolocation.ts` | Replaced by
centralised controller |
| `app/components/UI/Ramp/hooks/useDetectGeolocation.test.ts` | Tests
for deleted hook |

**Modified — Engine registration (5 files):**
- `package.json` — added `@metamask/geolocation-controller` dependency
- `app/core/Engine/Engine.ts` — registered both init functions
- `app/core/Engine/types.ts` — added to `Controllers`, `EngineState`,
`ControllersToInitialize`, `GlobalActions`, `GlobalEvents`
- `app/core/Engine/constants.ts` — added
`GeolocationController:stateChange`
- `app/core/Engine/messengers/index.ts` — registered both messenger
factories

**Modified — Consumer migration (9 files):**
- `app/components/UI/Ramp/index.tsx` — removed `useDetectGeolocation()`
call
- `app/reducers/fiatOrders/index.ts` — redefined
`getDetectedGeolocation` to read from controller
- `app/controllers/perps/PerpsController.ts` — calls
`GeolocationController:getGeolocation` via messenger
- `app/controllers/perps/services/EligibilityService.ts` — removed all
geolocation-fetching code
- `app/controllers/perps/types/index.ts` — added `geoLocation` to
`CheckEligibilityParams`
- `app/core/Engine/messengers/perps-controller-messenger/index.ts` —
delegated `GeolocationController:getGeolocation`
-
`app/core/Engine/controllers/rewards-controller/services/rewards-data-service.ts`
— removed `fetchGeoLocation()`, `GEOLOCATION_URLS`, action type
- `app/core/Engine/controllers/rewards-controller/services/index.ts` —
removed re-export
- `app/core/Engine/controllers/rewards-controller/RewardsController.ts`
— calls `GeolocationController:getGeolocation`
- `app/core/Engine/messengers/rewards-controller-messenger/index.ts` —
added/removed delegated actions
- `app/components/UI/Card/sdk/CardSDK.ts` — removed `getGeoLocation()`
method
- `app/components/UI/Card/util/getCardholder.ts` — reads from
`Engine.context`

**Modified — Tests (5 files):**
- `app/controllers/perps/services/EligibilityService.test.ts` — tests
`checkEligibility` with `geoLocation` param
- `app/controllers/perps/PerpsController.test.ts` — updated mock
-
`app/core/Engine/controllers/rewards-controller/services/rewards-data-service.test.ts`
— removed `fetchGeoLocation` tests
-
`app/core/Engine/controllers/rewards-controller/RewardsController.test.ts`
— updated messenger call references
- `app/components/UI/Card/sdk/CardSDK.test.ts` — removed
`getGeoLocation` tests
- `app/components/UI/Card/util/getCardholder.test.ts` — mocks
`Engine.context` instead of `cardSDK.getGeoLocation()`
- `app/reducers/fiatOrders/index.test.ts` — updated
`getDetectedGeolocation` tests

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Refs: https://consensyssoftware.atlassian.net/browse/MCWP-350

## **Manual testing steps**

```gherkin
Feature: Centralised geolocation via GeolocationController

  Scenario: Only one geolocation API call is made on app load
    Given the app is freshly launched
    When the Engine initialises
    Then exactly 1 request to on-ramp.api.cx.metamask.io/geolocation is made
    And no duplicate geolocation requests are made by Ramp, Perps, Rewards, or Card

  Scenario: Ramp region detection uses controller-sourced geolocation
    Given the user opens the Buy/Sell flow
    When the Ramp UI loads
    Then the detected region matches the user's actual location
    And no separate geolocation fetch is triggered by the Ramp hook

  Scenario: Perps eligibility check uses controller-sourced geolocation
    Given the user navigates to the Perps feature
    When eligibility is checked
    Then the eligibility result is correct for the user's region
    And EligibilityService does not make its own geolocation API call

  Scenario: Rewards geo-blocking uses controller-sourced geolocation
    Given the user accesses the Rewards feature
    When getGeoRewardsMetadata is called
    Then geo-blocking works correctly (UK/GB/GI users are blocked from opt-in)
    And RewardsDataService does not make its own geolocation API call

  Scenario: Card country detection uses controller-sourced geolocation
    Given the user accesses the Card feature
    When cardholder accounts are loaded
    Then the correct country code is returned
    And CardSDK does not make its own geolocation API call

  Scenario: Geolocation re-fetches after TTL expires
    Given geolocation was fetched more than 5 minutes ago
    When a consumer requests geolocation
    Then a fresh API call is made
    And the cached value is updated
```

## **Screenshots/Recordings**

### **Before**

N/A — no UI changes, backend/controller wiring only.

### **After**



https://github.com/user-attachments/assets/793c67b5-fbbd-48b9-a8e3-4e4924257c0b


N/A — no UI changes, backend/controller wiring only.

## **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]
> **Medium Risk**
> Touches Engine initialization and multiple feature flows
(Ramp/Perps/Rewards/Card) by changing the geolocation source and state
shape, so regressions could impact region-gating and routing decisions.
Logic is mostly wiring/migration, but it changes when/where geolocation
is fetched (eager on Engine start).
> 
> **Overview**
> Integrates `@metamask/geolocation-controller` into the mobile Engine
by registering `GeolocationApiService` and `GeolocationController`,
adding messengers/types/background-state plumbing, and **eagerly
fetching** geolocation on Engine startup.
> 
> Migrates geolocation consumers to the centralized controller: Ramp
drops `useDetectGeolocation` and `fiatOrders.detectedGeolocation`
(selector now reads
`engine.backgroundState.GeolocationController.location`), Perps passes
controller-provided `geoLocation` into
`EligibilityService.checkEligibility`, Rewards switches
`RewardsController` to call `GeolocationController:getGeolocation` and
removes `RewardsDataService.fetchGeoLocation`, and Card removes
`CardSDK.getGeoLocation` in favor of
`Engine.controllerMessenger.call('GeolocationController:getGeolocation')`
in `getCardholder`.
> 
> Updates unit/E2E tests, fixtures, and API mocks to the new state
location and ISO 3166-2 location code expectations.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f9d0630. 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>
<!--
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 is part of a series to remove the legacy code after BIP-44 was
shipped, several components, hooks, views, etc. still uses the remote
feature flag for BIP-44 causing an unnecessary overcomplexity that can
be easily removed.

The change includes updating the network hooks.

## **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/MUL-1461

## **Manual testing steps**

Not applicable

## **Screenshots/Recordings**

Not applicable

## **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]
> **Medium Risk**
> This removes feature-flag gating around network enablement/selection
hooks, so behavior now always follows the post-BIP-44 multichain path;
mistakes could impact which networks are considered enabled or how
enabling is applied. Changes are localized but touch core hooks used
across network selection UI.
> 
> **Overview**
> **Network hooks no longer branch on the BIP-44/multichain state2
remote flag.** `useNetworksToUse` always derives `networksToUse` and the
combined “all selected” state from selected internal accounts and
per-namespace network lists, and drops the
`isMultichainAccountsState2Enabled` return value.
> 
> `useNetworkEnablement` now always enables via
`NetworkEnablementController.enableNetwork` (no namespace-specific
fallback), and `useCurrentNetworkInfo` always flattens
`enabledNetworksByNamespace` when computing enabled networks.
> 
> Tests were updated to remove flag mocks/expectations, a smoke test for
adding popular networks was temporarily skipped pending BIP-44 UI
updates, and a debounce wait comment/timeout rationale was clarified in
`MultichainAccountSelectorList` tests.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8276e02. 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 Mar 10, 2026
@pull pull Bot added the ⤵️ pull label Mar 10, 2026
@pull pull Bot merged commit 89d2744 into Reality2byte:main Mar 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants