[pull] main from MetaMask:main#589
Merged
pull[bot] merged 3 commits intoReality2byte:mainfrom Mar 10, 2026
Merged
Conversation
<!--
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 -->
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 : )