Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,25 @@ module.exports = {
'android.debug': {
type: 'android.apk',
binaryPath: process.env.PREBUILT_ANDROID_APK_PATH || 'android/app/build/outputs/apk/prod/debug/app-prod-debug.apk',
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH,
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH || 'android/app/build/outputs/apk/androidTest/prod/debug/app-prod-debug-androidTest.apk',
build: 'export CONFIGURATION="Debug" && yarn build:android:main:e2e',
},
'android.release': {
type: 'android.apk',
binaryPath: process.env.PREBUILT_ANDROID_APK_PATH || 'android/app/build/outputs/apk/prod/release/app-prod-release.apk',
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH,
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH || 'android/app/build/outputs/apk/androidTest/prod/release/app-prod-release-androidTest.apk',
build: `export CONFIGURATION="Release" && yarn build:android:main:e2e`,
},
'android.flask.debug': {
type: 'android.apk',
binaryPath: process.env.PREBUILT_ANDROID_APK_PATH || 'android/app/build/outputs/apk/flask/debug/app-flask-debug.apk',
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH,
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH || 'android/app/build/outputs/apk/androidTest/flask/debug/app-flask-debug-androidTest.apk',
build: 'export CONFIGURATION="Debug" && yarn build:android:flask:e2e',
},
'android.flask.release': {
type: 'android.apk',
binaryPath: process.env.PREBUILT_ANDROID_APK_PATH || 'android/app/build/outputs/apk/flask/release/app-flask-release.apk',
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH,
testBinaryPath: process.env.PREBUILT_ANDROID_TEST_APK_PATH || 'android/app/build/outputs/apk/androidTest/flask/release/app-flask-release-androidTest.apk',
build: `export CONFIGURATION="Release" && yarn build:android:flask:e2e`,
},
},
Expand Down
1 change: 0 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ app/core/Engine/types.ts @MetaMask/mobile-pla
app/core/Engine/controllers/remote-feature-flag-controller/ @MetaMask/mobile-platform
app/core/DeeplinkManager @MetaMask/mobile-platform
scripts/build.sh @MetaMask/mobile-platform
scripts/update-expo-channel.js @MetaMask/mobile-admins

# Platform & Snaps Code Fencing File
metro.transform.js @MetaMask/mobile-platform @MetaMask/core-platform
Expand Down
168 changes: 83 additions & 85 deletions .github/workflows/build-android-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ jobs:
echo "🚀 Setting up project..."
yarn setup:github-ci --no-build-ios

# Generate fingerprint AFTER setup but BEFORE any build modifications (the fingerprint now is fake we do not want the cached apk)
- name: Generate current fingerprint
id: generate-fingerprint
run: |
FINGERPRINT=$(yarn fingerprint:generate)
echo "fingerprint=$FINGERPRINT" >> "$GITHUB_OUTPUT"
echo "Current fingerprint: ${FINGERPRINT}"
# # Generate fingerprint AFTER setup but BEFORE any build modifications (the fingerprint now is fake we do not want the cached apk)
# - name: Generate current fingerprint
# id: generate-fingerprint
# run: |
# FINGERPRINT=$(yarn fingerprint:generate)
# echo "fingerprint=$FINGERPRINT" >> "$GITHUB_OUTPUT"
# echo "Current fingerprint: ${FINGERPRINT}"

- name: Determine target paths and Artifact Names
id: determine-target-paths
Expand All @@ -102,26 +102,25 @@ jobs:
exit 1
fi

- name: Check and restore cached APKs if Fingerprint is found
id: apk-cache-restore
uses: cirruslabs/cache@v4
with:
path: |
${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# Include Gradle properties in key to force rebuild when properties change
# Keep the `hashFiles` call for Gradle config in-sync with these steps:
# - "Cache Gradle dependencies"
# - "Cache build artifacts"
key: android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-${{ steps.generate-fingerprint.outputs.fingerprint }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-
android-apk-
# - name: Check and restore cached APKs if Fingerprint is found
# id: apk-cache-restore
# uses: cirruslabs/cache@v4
# with:
# path: |
# ${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
# ${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
# ${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# # Include Gradle properties in key to force rebuild when properties change
# # Keep the `hashFiles` call for Gradle config in-sync with these steps:
# # - "Cache Gradle dependencies"
# # - "Cache build artifacts"
# key: android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-${{ steps.generate-fingerprint.outputs.fingerprint }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
# restore-keys: |
# android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-
# android-apk-

- name: Cache Gradle dependencies
uses: cirruslabs/cache@v4
if: ${{ steps.apk-cache-restore.outputs.cache-hit != 'true' }}
env:
GRADLE_CACHE_VERSION: 1
with:
Expand All @@ -134,7 +133,6 @@ jobs:
key: gradle-${{ env.GRADLE_CACHE_VERSION }}-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}

- name: Build Android E2E APKs
if: ${{ steps.apk-cache-restore.outputs.cache-hit != 'true' }}
run: |
echo "🏗 Building Android E2E APKs..."
export NODE_OPTIONS="--max-old-space-size=8192"
Expand Down Expand Up @@ -183,68 +181,68 @@ jobs:
GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }}
MM_INFURA_PROJECT_ID: ${{ secrets.MM_INFURA_PROJECT_ID }}

- name: Repack APK with JS updates using @expo/repack-app
if: ${{ steps.apk-cache-restore.outputs.cache-hit == 'true' }}
run: |
echo "📦 Repacking APK with updated JavaScript bundle using @expo/repack-app..."
# Use the optimized repack script which uses @expo/repack-app
yarn build:repack:android
echo "📦 Final APK size: $(du -h "${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk" | cut -f1)"
env:
PLATFORM: android
METAMASK_ENVIRONMENT: ${{ inputs.metamask_environment }}
METAMASK_BUILD_TYPE: ${{ inputs.build_type }}
IS_TEST: true
E2E: 'true'
IGNORE_BOXLOGS_DEVELOPMENT: true
GITHUB_CI: 'true'
CI: 'true'
NODE_OPTIONS: '--max-old-space-size=8192'
BRIDGE_USE_DEV_APIS: 'true'
RAMP_INTERNAL_BUILD: 'true'
SEEDLESS_ONBOARDING_ENABLED: 'true'
MM_NOTIFICATIONS_UI_ENABLED: 'true'
MM_SECURITY_ALERTS_API_ENABLED: 'true'
MM_REMOVE_GLOBAL_NETWORK_SELECTOR: 'true'
FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN: ${{ secrets.FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN }}
FEATURES_ANNOUNCEMENTS_SPACE_ID: ${{ secrets.FEATURES_ANNOUNCEMENTS_SPACE_ID }}
SEGMENT_WRITE_KEY_QA: ${{ secrets.SEGMENT_WRITE_KEY_QA }}
SEGMENT_WRITE_KEY_FLASK: ${{ secrets.SEGMENT_WRITE_KEY_FLASK }}
SEGMENT_PROXY_URL_QA: ${{ secrets.SEGMENT_PROXY_URL_QA }}
SEGMENT_PROXY_URL_FLASK: ${{ secrets.SEGMENT_PROXY_URL_FLASK }}
SEGMENT_DELETE_API_SOURCE_ID_QA: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_QA }}
SEGMENT_DELETE_API_SOURCE_ID_FLASK: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_FLASK }}
SEGMENT_REGULATIONS_ENDPOINT_QA: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_QA }}
SEGMENT_REGULATIONS_ENDPOINT_FLASK: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_FLASK }}
MM_SENTRY_DSN_TEST: ${{ secrets.MM_SENTRY_DSN_TEST }}
MM_SENTRY_AUTH_TOKEN: ${{ secrets.MM_SENTRY_AUTH_TOKEN }}
MAIN_IOS_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_IOS_GOOGLE_CLIENT_ID_UAT }}
FLASK_IOS_GOOGLE_CLIENT_ID_PROD: ${{ secrets.FLASK_IOS_GOOGLE_CLIENT_ID_PROD }}
MAIN_IOS_GOOGLE_REDIRECT_URI_UAT: ${{ secrets.MAIN_IOS_GOOGLE_REDIRECT_URI_UAT }}
FLASK_IOS_GOOGLE_REDIRECT_URI_PROD: ${{ secrets.FLASK_IOS_GOOGLE_REDIRECT_URI_PROD }}
MAIN_ANDROID_APPLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_APPLE_CLIENT_ID_UAT }}
FLASK_ANDROID_APPLE_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_APPLE_CLIENT_ID_PROD }}
MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT }}
FLASK_ANDROID_GOOGLE_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_GOOGLE_CLIENT_ID_PROD }}
MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT }}
FLASK_ANDROID_GOOGLE_SERVER_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_GOOGLE_SERVER_CLIENT_ID_PROD }}
GOOGLE_SERVICES_B64_IOS: ${{ secrets.GOOGLE_SERVICES_B64_IOS }}
GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }}
MM_INFURA_PROJECT_ID: ${{ secrets.MM_INFURA_PROJECT_ID }}
# - name: Repack APK with JS updates using @expo/repack-app
# if: ${{ steps.apk-cache-restore.outputs.cache-hit == 'true' }}
# run: |
# echo "📦 Repacking APK with updated JavaScript bundle using @expo/repack-app..."
# # Use the optimized repack script which uses @expo/repack-app
# yarn build:repack:android
# echo "📦 Final APK size: $(du -h "${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk" | cut -f1)"
# env:
# PLATFORM: android
# METAMASK_ENVIRONMENT: ${{ inputs.metamask_environment }}
# METAMASK_BUILD_TYPE: ${{ inputs.build_type }}
# IS_TEST: true
# E2E: 'true'
# IGNORE_BOXLOGS_DEVELOPMENT: true
# GITHUB_CI: 'true'
# CI: 'true'
# NODE_OPTIONS: '--max-old-space-size=8192'
# BRIDGE_USE_DEV_APIS: 'true'
# RAMP_INTERNAL_BUILD: 'true'
# SEEDLESS_ONBOARDING_ENABLED: 'true'
# MM_NOTIFICATIONS_UI_ENABLED: 'true'
# MM_SECURITY_ALERTS_API_ENABLED: 'true'
# MM_REMOVE_GLOBAL_NETWORK_SELECTOR: 'true'
# FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN: ${{ secrets.FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN }}
# FEATURES_ANNOUNCEMENTS_SPACE_ID: ${{ secrets.FEATURES_ANNOUNCEMENTS_SPACE_ID }}
# SEGMENT_WRITE_KEY_QA: ${{ secrets.SEGMENT_WRITE_KEY_QA }}
# SEGMENT_WRITE_KEY_FLASK: ${{ secrets.SEGMENT_WRITE_KEY_FLASK }}
# SEGMENT_PROXY_URL_QA: ${{ secrets.SEGMENT_PROXY_URL_QA }}
# SEGMENT_PROXY_URL_FLASK: ${{ secrets.SEGMENT_PROXY_URL_FLASK }}
# SEGMENT_DELETE_API_SOURCE_ID_QA: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_QA }}
# SEGMENT_DELETE_API_SOURCE_ID_FLASK: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_FLASK }}
# SEGMENT_REGULATIONS_ENDPOINT_QA: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_QA }}
# SEGMENT_REGULATIONS_ENDPOINT_FLASK: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_FLASK }}
# MM_SENTRY_DSN_TEST: ${{ secrets.MM_SENTRY_DSN_TEST }}
# MM_SENTRY_AUTH_TOKEN: ${{ secrets.MM_SENTRY_AUTH_TOKEN }}
# MAIN_IOS_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_IOS_GOOGLE_CLIENT_ID_UAT }}
# FLASK_IOS_GOOGLE_CLIENT_ID_PROD: ${{ secrets.FLASK_IOS_GOOGLE_CLIENT_ID_PROD }}
# MAIN_IOS_GOOGLE_REDIRECT_URI_UAT: ${{ secrets.MAIN_IOS_GOOGLE_REDIRECT_URI_UAT }}
# FLASK_IOS_GOOGLE_REDIRECT_URI_PROD: ${{ secrets.FLASK_IOS_GOOGLE_REDIRECT_URI_PROD }}
# MAIN_ANDROID_APPLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_APPLE_CLIENT_ID_UAT }}
# FLASK_ANDROID_APPLE_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_APPLE_CLIENT_ID_PROD }}
# MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT }}
# FLASK_ANDROID_GOOGLE_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_GOOGLE_CLIENT_ID_PROD }}
# MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT }}
# FLASK_ANDROID_GOOGLE_SERVER_CLIENT_ID_PROD: ${{ secrets.FLASK_ANDROID_GOOGLE_SERVER_CLIENT_ID_PROD }}
# GOOGLE_SERVICES_B64_IOS: ${{ secrets.GOOGLE_SERVICES_B64_IOS }}
# GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }}
# MM_INFURA_PROJECT_ID: ${{ secrets.MM_INFURA_PROJECT_ID }}

# Cache build artifacts with the pre-build fingerprint
- name: Cache build artifacts
if: ${{ steps.apk-cache-restore.outputs.cache-hit != 'true' }}
uses: cirruslabs/cache@v4
with:
path: |
${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# Keep the `hashFiles` call for Gradle config in-sync with these steps:
# - "Check and restore cached APKs if Fingerprint is found"
# - "Cache Gradle dependencies"
key: android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-${{ steps.generate-fingerprint.outputs.fingerprint }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
# - name: Cache build artifacts
# if: ${{ steps.apk-cache-restore.outputs.cache-hit != 'true' }}
# uses: cirruslabs/cache@v4
# with:
# path: |
# ${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
# ${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
# ${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# # Keep the `hashFiles` call for Gradle config in-sync with these steps:
# # - "Check and restore cached APKs if Fingerprint is found"
# # - "Cache Gradle dependencies"
# key: android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-${{ steps.generate-fingerprint.outputs.fingerprint }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}

- name: Upload Android APK
id: upload-apk
Expand Down
3 changes: 0 additions & 3 deletions .js.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ export WALLET_CONNECT_PROJECT_ID=""
# Default PORT for metro
export WATCHER_PORT=8081

# Expo Project ID for OTA updates
export EXPO_PROJECT_ID=""

# Environment: "production", "pre-release" or "dev"
export METAMASK_ENVIRONMENT="dev"

Expand Down
13 changes: 7 additions & 6 deletions android/gradle.properties.github
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# GitHub Actions-specific Gradle settings
# High-performance settings for 64GB/16CPU runners
# Optimized for E2E builds on GitHub Actions runners

# JVM configuration - high-performance for GitHub Actions
org.gradle.jvmargs=-Xmx32g -XX:MaxMetaspaceSize=2g -XX:+UseG1GC -XX:G1HeapRegionSize=32m -XX:+UseStringDeduplication -XX:+OptimizeStringConcat
# JVM configuration - balanced settings to avoid OOM while maintaining performance
# Using 16GB heap to leave room for parallel workers and native memory
org.gradle.jvmargs=-Xmx16g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+UseStringDeduplication -XX:+OptimizeStringConcat

# Enable all performance optimizations for GitHub Actions
# Enable performance optimizations but limit parallelism to prevent OOM
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.daemon=true
org.gradle.workers.max=12
org.gradle.vfs.watch=true
org.gradle.workers.max=6
org.gradle.vfs.watch=false

# CI-specific optimizations - enabled for GitHub Actions
kotlin.incremental=true
Expand Down
29 changes: 1 addition & 28 deletions app.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const { RUNTIME_VERSION, PROJECT_ID, UPDATE_URL } = require('./ota.config.js');

module.exports = {
name: 'MetaMask',
displayName: 'MetaMask',
Expand All @@ -17,9 +15,7 @@ module.exports = {
'../../node_modules/@notifee/react-native/android/libs',
],
},
ios: {
jsEngine: 'hermes',
},
ios: {},
},
],
[
Expand All @@ -40,28 +36,5 @@ module.exports = {
ios: {
bundleIdentifier: 'io.metamask.MetaMask',
usesAppleSignIn: true,
jsEngine: 'hermes',
},
expo: {
owner: 'metamask-test',
runtimeVersion: RUNTIME_VERSION,
updates: {
url: UPDATE_URL,
// Channel is set by requestHeaders, will be overridden with build script
requestHeaders: {
'expo-channel-name': 'preview',
},
},
extra: {
eas: {
projectId: PROJECT_ID,
},
},
android: {
package: 'io.metamask',
},
ios: {
bundleIdentifier: 'io.metamask.MetaMask',
},
},
};
34 changes: 0 additions & 34 deletions app/__mocks__/expo-updates.ts

This file was deleted.

17 changes: 16 additions & 1 deletion app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ import RewardsClaimBottomSheetModal from '../../UI/Rewards/components/Tabs/Level
import RewardOptInAccountGroupModal from '../../UI/Rewards/components/Settings/RewardOptInAccountGroupModal';
import ReferralBottomSheetModal from '../../UI/Rewards/components/ReferralBottomSheetModal';
import { selectRewardsSubscriptionId } from '../../../selectors/rewards';
import { getImportTokenNavbarOptions } from '../../UI/Navbar';
import {
TOKEN_TITLE,
NFT_TITLE,
TOKEN,
} from '../../Views/AddAsset/AddAsset.constants';
import { strings } from '../../../../locales/i18n';

const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
Expand Down Expand Up @@ -988,7 +995,15 @@ const MainNavigator = () => {
<Stack.Screen
name="AddAsset"
component={AddAsset}
options={{ headerShown: false }}
options={({ route, navigation }) => ({
...getImportTokenNavbarOptions(
navigation,
strings(
`add_asset.${route.params?.assetType === TOKEN ? TOKEN_TITLE : NFT_TITLE}`,
),
),
headerShown: true,
})}
/>
<Stack.Screen
name="ConfirmAddAsset"
Expand Down
Loading
Loading