From 0b810cdb7b23491073473cdead5df8f9a95fbb63 Mon Sep 17 00:00:00 2001 From: Nico MASSART Date: Mon, 19 May 2025 17:39:34 +0200 Subject: [PATCH 1/8] Chore/7.46.1 hotfix version update (#15455) update version of the app to match 7.46.1 hotfix branch --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a24a722c03c3..a9ad4c000d34 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -178,7 +178,7 @@ android { applicationId "io.metamask" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionName "7.46.0" + versionName "7.46.1" versionCode 1846 testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy 'react-native-camera', 'general' diff --git a/bitrise.yml b/bitrise.yml index 7af534cdbb6c..e3a96009c8a8 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2210,13 +2210,13 @@ app: PROJECT_LOCATION_IOS: ios - opts: is_expand: false - VERSION_NAME: 7.46.0 + VERSION_NAME: 7.46.1 - opts: is_expand: false VERSION_NUMBER: 1846 - opts: is_expand: false - FLASK_VERSION_NAME: 7.46.0 + FLASK_VERSION_NAME: 7.46.1 - opts: is_expand: false FLASK_VERSION_NUMBER: 1846 diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 5fa0462834dc..cf4d919c3609 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1299,7 +1299,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1365,7 +1365,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1432,7 +1432,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1496,7 +1496,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1657,7 +1657,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", @@ -1724,7 +1724,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.0; + MARKETING_VERSION = 7.46.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( "$(inherited)", diff --git a/package.json b/package.json index 22fd243350c9..b38541554d4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask", - "version": "7.46.0", + "version": "7.46.1", "private": true, "scripts": { "audit:ci": "./scripts/yarn-audit.sh", From ce7b7a580e8c7f76f75ecb5ecfda0afc973cede4 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Mon, 19 May 2025 15:50:44 +0000 Subject: [PATCH 2/8] Bump version number to 1859 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a9ad4c000d34..a396f8ab1b23 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -179,7 +179,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.46.1" - versionCode 1846 + versionCode 1859 testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy 'react-native-camera', 'general' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/bitrise.yml b/bitrise.yml index e3a96009c8a8..ed254e838718 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2213,13 +2213,13 @@ app: VERSION_NAME: 7.46.1 - opts: is_expand: false - VERSION_NUMBER: 1846 + VERSION_NUMBER: 1859 - opts: is_expand: false FLASK_VERSION_NAME: 7.46.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 1846 + FLASK_VERSION_NUMBER: 1859 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index cf4d919c3609..b1460af2c79e 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1261,7 +1261,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1330,7 +1330,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1395,7 +1395,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1461,7 +1461,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1620,7 +1620,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1689,7 +1689,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1846; + CURRENT_PROJECT_VERSION = 1859; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From fdc569a9659ea3bc572c63646d65f2632ad56ee0 Mon Sep 17 00:00:00 2001 From: tommasini <46944231+tommasini@users.noreply.github.com> Date: Mon, 19 May 2025 21:43:15 +0000 Subject: [PATCH 3/8] chore(runway): cherry-pick fix: capture exception with Sentry instead throwing the error (#15463) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Capture exception instead of throwing an error at the getPersistentState. This is a re implementation of what we have at the base controller. This is the longer term solution: https://github.com/MetaMask/core/issues/5545 ## **Related issues** Fixes: #15453 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **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. --------- Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com> --- .../getPersistentState.test.ts | 229 ++++++++++++++++++ .../getPersistentState/getPersistentState.ts | 62 +++++ app/store/persistConfig.ts | 2 +- 3 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 app/store/getPersistentState/getPersistentState.test.ts create mode 100644 app/store/getPersistentState/getPersistentState.ts diff --git a/app/store/getPersistentState/getPersistentState.test.ts b/app/store/getPersistentState/getPersistentState.test.ts new file mode 100644 index 000000000000..c899b3e5d886 --- /dev/null +++ b/app/store/getPersistentState/getPersistentState.test.ts @@ -0,0 +1,229 @@ +import { getPersistentState } from './getPersistentState'; +import { StateMetadata } from '@metamask/base-controller'; + +describe('getPersistentState', () => { + it('return empty state', () => { + expect(getPersistentState({}, {})).toStrictEqual({}); + }); + + it('return empty state when no properties are persistent', () => { + const persistentState = getPersistentState( + { count: 1 }, + { count: { anonymous: false, persist: false } }, + ); + expect(persistentState).toStrictEqual({}); + }); + + it('return persistent state', () => { + const persistentState = getPersistentState( + { + password: 'secret password', + privateKey: '123', + network: 'mainnet', + tokens: ['DAI', 'USDC'], + }, + { + password: { + anonymous: false, + persist: true, + }, + privateKey: { + anonymous: false, + persist: true, + }, + network: { + anonymous: false, + persist: false, + }, + tokens: { + anonymous: false, + persist: false, + }, + }, + ); + expect(persistentState).toStrictEqual({ + password: 'secret password', + privateKey: '123', + }); + }); + + it('use function to derive persistent state', () => { + const normalizeTransactionHash = (hash: string) => hash.toLowerCase(); + + const persistentState = getPersistentState( + { + transactionHash: '0X1234', + }, + { + transactionHash: { + anonymous: false, + persist: normalizeTransactionHash, + }, + }, + ); + + expect(persistentState).toStrictEqual({ transactionHash: '0x1234' }); + }); + + it('allow returning a partial object from a persist function', () => { + const getPersistentTxMeta = (txMeta: { hash: string; value: number }) => ({ + value: txMeta.value, + }); + + const persistentState = getPersistentState( + { + txMeta: { + hash: '0x123', + value: 10, + }, + }, + { + txMeta: { + anonymous: false, + persist: getPersistentTxMeta, + }, + }, + ); + + expect(persistentState).toStrictEqual({ txMeta: { value: 10 } }); + }); + + it('allow returning a nested partial object from a persist function', () => { + const getPersistentTxMeta = (txMeta: { + hash: string; + value: number; + history: { hash: string; value: number }[]; + }) => ({ + history: txMeta.history.map((entry) => ({ value: entry.value })), + value: txMeta.value, + }); + + const persistentState = getPersistentState( + { + txMeta: { + hash: '0x123', + history: [ + { + hash: '0x123', + value: 9, + }, + ], + value: 10, + }, + }, + { + txMeta: { + anonymous: false, + persist: getPersistentTxMeta, + }, + }, + ); + + expect(persistentState).toStrictEqual({ + txMeta: { history: [{ value: 9 }], value: 10 }, + }); + }); + + it('allow transforming types in a persist function', () => { + const persistentState = getPersistentState( + { + count: '1', + }, + { + count: { + anonymous: false, + persist: (count) => Number(count), + }, + }, + ); + + expect(persistentState).toStrictEqual({ count: 1 }); + }); + + // New test cases for the two key changes + + it('exclude state property when no metadata exists for a key', () => { + const state = { + password: 'secret password', + privateKey: '123', + network: 'mainnet', + }; + const metadata = { + password: { + anonymous: false, + persist: true, + }, + privateKey: { + anonymous: false, + persist: true, + }, + } as unknown as StateMetadata; + + const persistentState = getPersistentState(state, metadata); + + expect(persistentState).toStrictEqual({ + password: 'secret password', + privateKey: '123', + }); + }); + + it('exclude state property when an error occurs during derivation', () => { + const state = { + password: 'secret password', + privateKey: '123', + network: 'mainnet', + }; + const metadata = { + password: { + anonymous: false, + persist: true, + }, + privateKey: { + anonymous: false, + persist: () => { + throw new Error('Derivation error'); + }, + }, + } as unknown as StateMetadata; + + const persistentState = getPersistentState(state, metadata); + + expect(persistentState).toStrictEqual({ + password: 'secret password', + }); + }); + + it('exclude nested objects without metadata', () => { + const state = { + user: { + name: 'John', + settings: { + theme: 'dark', + notifications: true, + }, + }, + preferences: { + language: 'en', + currency: 'USD', + }, + }; + const metadata = { + user: { + anonymous: false, + persist: true, + }, + } as unknown as StateMetadata; + + const persistentState = getPersistentState(state, metadata); + + expect(persistentState).toStrictEqual({ + user: { + name: 'John', + settings: { + theme: 'dark', + notifications: true, + }, + }, + }); + }); +}); diff --git a/app/store/getPersistentState/getPersistentState.ts b/app/store/getPersistentState/getPersistentState.ts new file mode 100644 index 000000000000..7b2797fd22fb --- /dev/null +++ b/app/store/getPersistentState/getPersistentState.ts @@ -0,0 +1,62 @@ +import { StateConstraint, StateMetadata } from '@metamask/base-controller'; +import { Json } from '@metamask/utils'; +import { captureException } from '@sentry/react-native'; + +/**! IMPORTANT: THIS IS MEANT TO BE TEMPORARY, WE SHOULD NOT USE THIS AND USE THE VERSION IN BASE-CONTROLLER INSTEAD. + * The reason why we are using this now, it's because we have controllers state without metadata. + * An epic will be created to add the metadata to the controllers state without it. + * As an example of a property without metadata: "swapsTransactions" in TransactionController + */ + +/** + * Returns the subset of state that should be persisted. + * + * @param state - The controller state. + * @param metadata - The controller state metadata, which describes which pieces of state should be persisted. + * @returns The subset of controller state that should be persisted. + */ +export function getPersistentState( + state: ControllerState, + metadata: StateMetadata, +): Record { + return deriveStateFromMetadata(state, metadata, 'persist'); +} + +/** + * Use the metadata to derive state according to the given metadata property. + * + * @param state - The full controller state. + * @param metadata - The controller metadata. + * @param metadataProperty - The metadata property to use to derive state. + * @returns The metadata-derived controller state. + */ +function deriveStateFromMetadata( + state: ControllerState, + metadata: StateMetadata, + metadataProperty: 'anonymous' | 'persist', +): Record { + return (Object.keys(state) as (keyof ControllerState)[]).reduce< + Record + >((derivedState, key) => { + try { + const stateMetadata = metadata[key]; + if (!stateMetadata) { + throw new Error(`No metadata found for '${String(key)}'`); + } + const propertyMetadata = stateMetadata[metadataProperty]; + const stateProperty = state[key]; + if (typeof propertyMetadata === 'function') { + derivedState[key] = propertyMetadata(stateProperty); + } else if (propertyMetadata) { + derivedState[key] = stateProperty; + } + return derivedState; + } catch (error) { + // This is what change from the original base controller implementation + // This is a temporary solution to capture the error for extraneous data that we did not detect + captureException(error as unknown as Error); + + return derivedState; + } + }, {} as never); +} diff --git a/app/store/persistConfig.ts b/app/store/persistConfig.ts index 9ef274fa40dc..0322cf6fee23 100644 --- a/app/store/persistConfig.ts +++ b/app/store/persistConfig.ts @@ -9,7 +9,7 @@ import Device from '../util/device'; import { UserState } from '../reducers/user'; import { debounce } from 'lodash'; import Engine, { EngineContext } from '../core/Engine'; -import { getPersistentState } from '@metamask/base-controller'; +import { getPersistentState } from './getPersistentState/getPersistentState'; const TIMEOUT = 40000; const STORAGE_DEBOUNCE_DELAY = 200; From 7ea2c7f330831fb378fd7a7e1a236b19a6a6eb4e Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Mon, 19 May 2025 22:47:37 +0000 Subject: [PATCH 4/8] Bump version number to 1862 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a396f8ab1b23..e31dcdceae8d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -179,7 +179,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.46.1" - versionCode 1859 + versionCode 1862 testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy 'react-native-camera', 'general' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/bitrise.yml b/bitrise.yml index ed254e838718..e46c585faa75 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2213,13 +2213,13 @@ app: VERSION_NAME: 7.46.1 - opts: is_expand: false - VERSION_NUMBER: 1859 + VERSION_NUMBER: 1862 - opts: is_expand: false FLASK_VERSION_NAME: 7.46.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 1859 + FLASK_VERSION_NUMBER: 1862 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index b1460af2c79e..f92b059954ab 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1261,7 +1261,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1330,7 +1330,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1395,7 +1395,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1461,7 +1461,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1620,7 +1620,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1689,7 +1689,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1859; + CURRENT_PROJECT_VERSION = 1862; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From ba06d2aca4f6d773f8c75601b0905bb4d1901989 Mon Sep 17 00:00:00 2001 From: "runway-github[bot]" <73448015+runway-github[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 06:31:49 +0200 Subject: [PATCH 5/8] chore(runway): cherry-pick fix: Sanitize gas values before transaction updater functions (#15699) --- .../confirm/confirm-component.test.tsx | 3 + .../components/info-root/info-root.test.tsx | 3 + app/util/transaction-controller/index.test.ts | 389 +++++++++++++++++- app/util/transaction-controller/index.ts | 57 +++ 4 files changed, 443 insertions(+), 9 deletions(-) diff --git a/app/components/Views/confirmations/components/confirm/confirm-component.test.tsx b/app/components/Views/confirmations/components/confirm/confirm-component.test.tsx index a32d3204dab4..247115f32891 100644 --- a/app/components/Views/confirmations/components/confirm/confirm-component.test.tsx +++ b/app/components/Views/confirmations/components/confirm/confirm-component.test.tsx @@ -87,6 +87,9 @@ jest.mock('../../../../../core/Engine', () => ({ }, }, }, + TransactionController: { + getTransactions: jest.fn().mockReturnValue([]), + }, }, controllerMessenger: { subscribe: jest.fn(), diff --git a/app/components/Views/confirmations/components/info-root/info-root.test.tsx b/app/components/Views/confirmations/components/info-root/info-root.test.tsx index ed387371e650..2e5fb0ab192d 100644 --- a/app/components/Views/confirmations/components/info-root/info-root.test.tsx +++ b/app/components/Views/confirmations/components/info-root/info-root.test.tsx @@ -44,6 +44,9 @@ jest.mock('../../../../../core/Engine', () => ({ }, }, }, + TransactionController: { + getTransactions: jest.fn().mockReturnValue([]), + }, }, controllerMessenger: { subscribe: jest.fn(), diff --git a/app/util/transaction-controller/index.test.ts b/app/util/transaction-controller/index.test.ts index 2b3e648c5358..320631743c84 100644 --- a/app/util/transaction-controller/index.test.ts +++ b/app/util/transaction-controller/index.test.ts @@ -1,4 +1,10 @@ -import { WalletDevice } from '@metamask/transaction-controller'; +import { + GasFeeEstimateType, + WalletDevice, + type TransactionMeta, + TransactionEnvelopeType, +} from '@metamask/transaction-controller'; +import { cloneDeep } from 'lodash'; //eslint-disable-next-line import/no-namespace import * as TransactionControllerUtils from './index'; import Engine from '../../core/Engine'; @@ -21,9 +27,48 @@ jest.mock('../../store', () => ({ }, })); -const TRANSACTION_MOCK = { from: '0x0', to: '0x1', value: '0x0' }; +const ID_MOCK = 'testId'; + +const EIP_1559_TRANSACTION_PARAMS_MOCK = { + from: '0x1559From', + to: '0x1559To', + value: '0x1559Value', + type: TransactionEnvelopeType.feeMarket, + gas: '0x1559Gas', + maxFeePerGas: '0x1559MaxFeePerGas', + maxPriorityFeePerGas: '0x1559MaxPriorityFeePerGas', +}; + +const LEGACY_TRANSACTION_PARAMS_MOCK = { + from: '0xlegacyFrom', + to: '0xlegacyTo', + value: '0xlegacyValue', + type: TransactionEnvelopeType.legacy, + gas: '0xlegacyGas', + gasPrice: '0xlegacyGasPrice', +}; + const NETWORK_CLIENT_ID_MOCK = 'testNetworkClientId'; +const EIP1559_TRANSACTION_META_MOCK = { + id: ID_MOCK, + txParams: EIP_1559_TRANSACTION_PARAMS_MOCK, +} as TransactionMeta; + +const GAS_PRICE_MOCK = '0x1234'; +const EIP1559_TRANSACTION_META_MOCK_WITH_GAS_FEE_ESTIMATES = { + ...EIP1559_TRANSACTION_META_MOCK, + gasFeeEstimates: { + type: GasFeeEstimateType.GasPrice, + gasPrice: GAS_PRICE_MOCK, + }, +} as unknown as TransactionMeta; + +const LEGACY_TRANSACTION_META_MOCK = { + id: ID_MOCK, + txParams: LEGACY_TRANSACTION_PARAMS_MOCK, +} as TransactionMeta; + const TRANSACTION_OPTIONS_MOCK = { deviceConfirmedOn: WalletDevice.MM_MOBILE, networkClientId: NETWORK_CLIENT_ID_MOCK, @@ -33,6 +78,7 @@ const TRANSACTION_OPTIONS_MOCK = { jest.mock('../../core/Engine', () => ({ context: { TransactionController: { + getTransactions: jest.fn(), addTransaction: jest.fn(), estimateGas: jest.fn(), estimateGasFee: jest.fn(), @@ -59,35 +105,47 @@ describe('Transaction Controller Util', () => { describe('addTransaction', () => { it('should call addTransaction with correct parameters', async () => { - await addTransaction(TRANSACTION_MOCK, TRANSACTION_OPTIONS_MOCK); + await addTransaction( + EIP_1559_TRANSACTION_PARAMS_MOCK, + TRANSACTION_OPTIONS_MOCK, + ); expect( Engine.context.TransactionController.addTransaction, - ).toHaveBeenCalledWith(TRANSACTION_MOCK, TRANSACTION_OPTIONS_MOCK); + ).toHaveBeenCalledWith( + EIP_1559_TRANSACTION_PARAMS_MOCK, + TRANSACTION_OPTIONS_MOCK, + ); }); }); describe('estimateGas', () => { it('should call estimateGas with correct parameters', async () => { - await estimateGas(TRANSACTION_MOCK, NETWORK_CLIENT_ID_MOCK); + await estimateGas( + EIP_1559_TRANSACTION_PARAMS_MOCK, + NETWORK_CLIENT_ID_MOCK, + ); expect( Engine.context.TransactionController.estimateGas, - ).toHaveBeenCalledWith(TRANSACTION_MOCK, NETWORK_CLIENT_ID_MOCK); + ).toHaveBeenCalledWith( + EIP_1559_TRANSACTION_PARAMS_MOCK, + NETWORK_CLIENT_ID_MOCK, + ); }); }); describe('estimateGasFee', () => { it('should call estimateGasFee with correct parameters', async () => { await estimateGasFee({ - transactionParams: TRANSACTION_MOCK, + transactionParams: EIP_1559_TRANSACTION_PARAMS_MOCK, chainId: '0x1', }); expect( Engine.context.TransactionController.estimateGasFee, ).toHaveBeenCalledWith({ - transactionParams: TRANSACTION_MOCK, + transactionParams: EIP_1559_TRANSACTION_PARAMS_MOCK, chainId: '0x1', }); }); @@ -98,7 +156,9 @@ describe('Transaction Controller Util', () => { const proxyMethodsKeys = Object.keys(proxyMethods); proxyMethodsKeys.forEach((key) => { const proxyMethod = proxyMethods[key as keyof typeof proxyMethods]; - proxyMethod(); + // This is to avoid type errors when calling the proxy method, no type can be inferred as this is existence check + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proxyMethod({} as any); expect( Engine.context.TransactionController[ key as keyof typeof proxyMethods @@ -200,4 +260,315 @@ describe('Transaction Controller Util', () => { expect(releaseLockMock).toHaveBeenCalledTimes(1); }); }); + + describe('Gas property sanitization', () => { + function testSanitization({ + expectedCallArgumentIndex = 0, + paramToSanitize = [], + testName, + transactionMock, + updaterFunction, + updaterFunctionName, + updaterFunctionParams, + }: { + expectedCallArgumentIndex?: number; + paramToSanitize?: string[]; + testName: string; + transactionMock: TransactionMeta; + updaterFunction: (...args: Params) => void; + updaterFunctionName: 'updateTransaction' | 'updateEditableParams'; + updaterFunctionParams: Params; + }) { + it(testName, () => { + const clonedUpdaterFunctionParams = cloneDeep(updaterFunctionParams); + + mockGetTransactions([transactionMock]); + + updaterFunction(...clonedUpdaterFunctionParams); + + const calledTxParams = ( + Engine.context.TransactionController?.[ + updaterFunctionName as keyof typeof Engine.context.TransactionController + ] as jest.Mock + ).mock.calls[0][expectedCallArgumentIndex]; + + // Either check txParams or the whole object + const calledTxParamsArguments = + calledTxParams?.txParams || calledTxParams; + + paramToSanitize.forEach((param: string) => { + expect(calledTxParamsArguments[param]).not.toBeDefined(); + }); + + expect(calledTxParamsArguments.type).toBe( + transactionMock.txParams.type, + ); + }); + } + + function mockGetTransactions(transactions: TransactionMeta[]) { + jest + .spyOn(Engine.context.TransactionController, 'getTransactions') + .mockReturnValue(transactions); + } + + describe('updates 1559 gas properties when gasPrice estimation is used', () => { + it('uses requestedTransactionParamsToUpdate values if provided', () => { + const customMaxFeePerGas = '0x5678'; + const customMaxPriorityFeePerGas = '0x9abc'; + + mockGetTransactions([ + EIP1559_TRANSACTION_META_MOCK_WITH_GAS_FEE_ESTIMATES, + ]); + + TransactionControllerUtils.updateTransaction( + { + id: ID_MOCK, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + maxFeePerGas: customMaxFeePerGas, + maxPriorityFeePerGas: customMaxPriorityFeePerGas, + }, + } as TransactionMeta, + 'testNote', + ); + + const updatedParams = ( + Engine.context.TransactionController.updateTransaction as jest.Mock + ).mock.calls[0][0].txParams; + expect(updatedParams.maxFeePerGas).toBe(customMaxFeePerGas); + expect(updatedParams.maxPriorityFeePerGas).toBe( + customMaxPriorityFeePerGas, + ); + }); + + it('falls back to existing txParams values if requested values are not provided', () => { + const existingMaxFeePerGas = '0xabcd'; + const existingMaxPriorityFeePerGas = '0xef01'; + + // Create transaction meta with txParams and gasPrice estimation + const transactionWithExistingParams = { + ...EIP1559_TRANSACTION_META_MOCK_WITH_GAS_FEE_ESTIMATES, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + maxFeePerGas: existingMaxFeePerGas, + maxPriorityFeePerGas: existingMaxPriorityFeePerGas, + }, + } as unknown as TransactionMeta; + + mockGetTransactions([transactionWithExistingParams]); + + // Call without fee values in the requested update + TransactionControllerUtils.updateTransaction( + { + id: ID_MOCK, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, + }, + } as TransactionMeta, + 'testNote', + ); + + // Verify fallback to existing txParams values + const updatedParams = ( + Engine.context.TransactionController.updateTransaction as jest.Mock + ).mock.calls[0][0].txParams; + expect(updatedParams.maxFeePerGas).toBe(existingMaxFeePerGas); + expect(updatedParams.maxPriorityFeePerGas).toBe( + existingMaxPriorityFeePerGas, + ); + }); + + it('falls back to gasPrice estimation if neither requested nor existing values are available', () => { + // Create transaction meta with only gasPrice estimation + const transactionWithOnlyGasPriceEstimate = { + ...EIP1559_TRANSACTION_META_MOCK_WITH_GAS_FEE_ESTIMATES, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, + }, + } as TransactionMeta; + + mockGetTransactions([transactionWithOnlyGasPriceEstimate]); + + // Call without fee values + TransactionControllerUtils.updateTransaction( + { + id: ID_MOCK, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, + }, + } as TransactionMeta, + 'testNote', + ); + + // Verify fallback to gasPrice estimation + const updatedParams = ( + Engine.context.TransactionController.updateTransaction as jest.Mock + ).mock.calls[0][0].txParams; + expect(updatedParams.maxFeePerGas).toBe(GAS_PRICE_MOCK); + expect(updatedParams.maxPriorityFeePerGas).toBe(GAS_PRICE_MOCK); + }); + + it('does not update 1559 gas properties when gasPrice estimation is not used', () => { + // Create transaction meta without gasPrice estimation + const transactionWithoutGasPriceEstimate = { + ...EIP1559_TRANSACTION_META_MOCK_WITH_GAS_FEE_ESTIMATES, + gasFeeEstimates: { + type: 'fee-market', // Not gasPrice + someOtherProperty: 'value', + }, + } as unknown as TransactionMeta; + + mockGetTransactions([transactionWithoutGasPriceEstimate]); + + TransactionControllerUtils.updateTransaction( + { + id: ID_MOCK, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + }, + } as unknown as TransactionMeta, + 'testNote', + ); + + // Verify that the transaction was passed as is + const updatedParams = ( + Engine.context.TransactionController.updateTransaction as jest.Mock + ).mock.calls[0][0].txParams; + expect(updatedParams).toEqual(EIP_1559_TRANSACTION_PARAMS_MOCK); + }); + }); + + describe('does not modify transaction params', () => { + it('when transaction is not exist', () => { + mockGetTransactions([]); + + TransactionControllerUtils.updateTransaction( + EIP1559_TRANSACTION_META_MOCK, + 'testNote', + ); + + expect( + Engine.context.TransactionController.updateTransaction, + ).toHaveBeenCalledWith(EIP1559_TRANSACTION_META_MOCK, 'testNote'); + + TransactionControllerUtils.updateEditableParams( + ID_MOCK, + EIP_1559_TRANSACTION_PARAMS_MOCK, + ); + + expect( + Engine.context.TransactionController.updateEditableParams, + ).toHaveBeenCalledWith(ID_MOCK, EIP_1559_TRANSACTION_PARAMS_MOCK); + }); + + it('when requested transaction params changes does not exist', () => { + mockGetTransactions([EIP1559_TRANSACTION_META_MOCK]); + + TransactionControllerUtils.updateTransaction( + { id: ID_MOCK } as TransactionMeta, + 'testNote', + ); + + expect( + Engine.context.TransactionController.updateTransaction, + ).toHaveBeenCalledWith({ id: ID_MOCK } as TransactionMeta, 'testNote'); + + TransactionControllerUtils.updateEditableParams( + ID_MOCK, + undefined as unknown as Parameters< + typeof TransactionControllerUtils.updateEditableParams + >[1], + ); + + expect( + Engine.context.TransactionController.updateEditableParams, + ).toHaveBeenCalledWith( + ID_MOCK, + undefined as unknown as Parameters< + typeof TransactionControllerUtils.updateEditableParams + >[1], + ); + }); + + it('when transaction type is not legacy or feeMarket', () => { + mockGetTransactions([ + { + ...EIP1559_TRANSACTION_META_MOCK, + txParams: { + ...EIP_1559_TRANSACTION_PARAMS_MOCK, + type: '0x4', + }, + }, + ]); + + TransactionControllerUtils.updateTransaction( + EIP1559_TRANSACTION_META_MOCK, + 'testNote', + ); + + expect( + Engine.context.TransactionController.updateTransaction, + ).toHaveBeenCalledWith(EIP1559_TRANSACTION_META_MOCK, 'testNote'); + + TransactionControllerUtils.updateEditableParams( + ID_MOCK, + EIP_1559_TRANSACTION_PARAMS_MOCK, + ); + + expect( + Engine.context.TransactionController.updateEditableParams, + ).toHaveBeenCalledWith(ID_MOCK, EIP_1559_TRANSACTION_PARAMS_MOCK); + }); + }); + + testSanitization({ + expectedCallArgumentIndex: 0, + paramToSanitize: ['maxFeePerGas', 'maxPriorityFeePerGas'], + testName: + 'updateTransaction removes maxFeePerGas and maxPriorityFeePerGas values for legacy transactions', + transactionMock: LEGACY_TRANSACTION_META_MOCK, + updaterFunction: TransactionControllerUtils.updateTransaction, + updaterFunctionName: 'updateTransaction', + updaterFunctionParams: [EIP1559_TRANSACTION_META_MOCK, 'testNote'], + }); + + testSanitization({ + expectedCallArgumentIndex: 0, + paramToSanitize: ['gasPrice'], + testName: 'updateTransaction removes gasPrice for EIP-1559 transactions', + transactionMock: EIP1559_TRANSACTION_META_MOCK, + updaterFunction: TransactionControllerUtils.updateTransaction, + updaterFunctionName: 'updateTransaction', + updaterFunctionParams: [EIP1559_TRANSACTION_META_MOCK, 'testNote'], + }); + + testSanitization({ + expectedCallArgumentIndex: 1, + paramToSanitize: ['maxFeePerGas', 'maxPriorityFeePerGas'], + testName: + 'updateEditableParams removes maxFeePerGas and maxPriorityFeePerGas values for legacy transactions', + transactionMock: LEGACY_TRANSACTION_META_MOCK, + updaterFunction: TransactionControllerUtils.updateEditableParams, + updaterFunctionName: 'updateEditableParams', + updaterFunctionParams: [ID_MOCK, EIP_1559_TRANSACTION_PARAMS_MOCK], + }); + + testSanitization({ + expectedCallArgumentIndex: 1, + paramToSanitize: ['gasPrice'], + testName: + 'updateEditableParams removes gasPrice for EIP-1559 transactions', + transactionMock: EIP1559_TRANSACTION_META_MOCK, + updaterFunction: TransactionControllerUtils.updateEditableParams, + updaterFunctionName: 'updateEditableParams', + updaterFunctionParams: [ID_MOCK, EIP_1559_TRANSACTION_PARAMS_MOCK], + }); + }); }); diff --git a/app/util/transaction-controller/index.ts b/app/util/transaction-controller/index.ts index 5c8ffccb6eee..a6fc460aa28e 100644 --- a/app/util/transaction-controller/index.ts +++ b/app/util/transaction-controller/index.ts @@ -1,5 +1,7 @@ import { + GasFeeEstimateType, TransactionParams, + TransactionEnvelopeType, TransactionController as BaseTransactionController, } from '@metamask/transaction-controller'; import { Hex } from '@metamask/utils'; @@ -102,6 +104,12 @@ export function updateTransaction( ...args: Parameters ) { const { TransactionController } = Engine.context; + const { txParams, id } = args[0]; + + // This is a temporary fix to ensure legacy transaction confirmations does not override expected gas properties + // Once redesign is complete, this can be removed + sanitizeTransactionParamsGasValues(id, txParams); + return TransactionController.updateTransaction(...args); } @@ -116,6 +124,13 @@ export function updateEditableParams( ...args: Parameters ) { const { TransactionController } = Engine.context; + const id = args[0]; + const txParams = args[1]; + + // This is a temporary fix to ensure legacy transaction confirmations does not override expected gas properties + // Once redesign is complete, this can be removed + sanitizeTransactionParamsGasValues(id, txParams); + return TransactionController.updateEditableParams(...args); } @@ -129,3 +144,45 @@ export const getNetworkNonce = async ( return nextNonce; }; + +function sanitizeTransactionParamsGasValues( + transactionId: string, + requestedTransactionParamsToUpdate: Partial, +) { + const { TransactionController } = Engine.context; + + const transactionMeta = TransactionController?.getTransactions({ + searchCriteria: { id: transactionId }, + })?.[0]; + + if (!transactionMeta || !requestedTransactionParamsToUpdate) { + return; + } + + const envelopeType = transactionMeta.txParams.type; + + if (envelopeType === TransactionEnvelopeType.legacy) { + requestedTransactionParamsToUpdate.type = TransactionEnvelopeType.legacy; + delete requestedTransactionParamsToUpdate.maxFeePerGas; + delete requestedTransactionParamsToUpdate.maxPriorityFeePerGas; + } else if (envelopeType === TransactionEnvelopeType.feeMarket) { + requestedTransactionParamsToUpdate.type = TransactionEnvelopeType.feeMarket; + if ( + transactionMeta?.gasFeeEstimates?.type === GasFeeEstimateType.GasPrice + ) { + // Try picking 1559 gas properties in order to ensure legacy transaction confirmations is setting expected gas properties + // 1. Requested change + // 2. Existing txParams + // 3. Existing gasFeeEstimates + requestedTransactionParamsToUpdate.maxFeePerGas = + requestedTransactionParamsToUpdate?.maxFeePerGas || + transactionMeta?.txParams?.maxFeePerGas || + transactionMeta?.gasFeeEstimates?.gasPrice; + requestedTransactionParamsToUpdate.maxPriorityFeePerGas = + requestedTransactionParamsToUpdate?.maxPriorityFeePerGas || + transactionMeta?.txParams?.maxPriorityFeePerGas || + transactionMeta?.gasFeeEstimates?.gasPrice; + } + delete requestedTransactionParamsToUpdate.gasPrice; + } +} From ee7bade7a6b4eeeddc00d6121b72a32310fda8f2 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Tue, 27 May 2025 07:36:03 +0000 Subject: [PATCH 6/8] Bump version number to 1891 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index e31dcdceae8d..1a00bb7aa99e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -179,7 +179,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.46.1" - versionCode 1862 + versionCode 1891 testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy 'react-native-camera', 'general' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/bitrise.yml b/bitrise.yml index e46c585faa75..0cad2f0704d5 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2213,13 +2213,13 @@ app: VERSION_NAME: 7.46.1 - opts: is_expand: false - VERSION_NUMBER: 1862 + VERSION_NUMBER: 1891 - opts: is_expand: false FLASK_VERSION_NAME: 7.46.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 1862 + FLASK_VERSION_NUMBER: 1891 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index f92b059954ab..a30b5dc1b3a5 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1261,7 +1261,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1330,7 +1330,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1395,7 +1395,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1461,7 +1461,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1620,7 +1620,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1689,7 +1689,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1862; + CURRENT_PROJECT_VERSION = 1891; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From 1770de71fe6e9879dd0e3c21a23f9ef2ded61d27 Mon Sep 17 00:00:00 2001 From: Nico MASSART Date: Tue, 27 May 2025 10:20:56 +0200 Subject: [PATCH 7/8] Chore: update version to 7.46.2 (#15708) update RC version to 7.46.2 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1a00bb7aa99e..3deecc2f9d19 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -178,7 +178,7 @@ android { applicationId "io.metamask" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionName "7.46.1" + versionName "7.46.2" versionCode 1891 testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy 'react-native-camera', 'general' diff --git a/bitrise.yml b/bitrise.yml index 0cad2f0704d5..e3110730e84b 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2210,13 +2210,13 @@ app: PROJECT_LOCATION_IOS: ios - opts: is_expand: false - VERSION_NAME: 7.46.1 + VERSION_NAME: 7.46.2 - opts: is_expand: false VERSION_NUMBER: 1891 - opts: is_expand: false - FLASK_VERSION_NAME: 7.46.1 + FLASK_VERSION_NAME: 7.46.2 - opts: is_expand: false FLASK_VERSION_NUMBER: 1891 diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index a30b5dc1b3a5..2dccede77084 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1299,7 +1299,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1365,7 +1365,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1432,7 +1432,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1496,7 +1496,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1657,7 +1657,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", @@ -1724,7 +1724,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.46.1; + MARKETING_VERSION = 7.46.2; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( "$(inherited)", diff --git a/package.json b/package.json index b38541554d4f..2a1eef63c3f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask", - "version": "7.46.1", + "version": "7.46.2", "private": true, "scripts": { "audit:ci": "./scripts/yarn-audit.sh", From af6bf7970ae350c8be6884157b5329785a66b0a4 Mon Sep 17 00:00:00 2001 From: sethkfman Date: Tue, 27 May 2025 16:12:03 -0600 Subject: [PATCH 8/8] update changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 821dd8f116a0..9262f31d58c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - fix(bridge): show "Auto" slippage for Solana swaps +## [7.46.2] + +### Fixed +- fix: Json rpc and invalid transaction type envelope errors ([#15538](https://github.com/MetaMask/metamask-mobile/pull/15538) + ## [7.46.1] ### Fixed @@ -5410,7 +5415,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957) - [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954) -[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.46.1...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.46.2...HEAD +[7.46.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.46.1...v7.46.2 [7.46.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.46.0...v7.46.1 [7.46.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.45.1...v7.46.0 [7.45.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.45.0...v7.45.1