diff --git a/.github/scripts/package.json b/.github/scripts/package.json index f4a890909e0..85ec37a545b 100644 --- a/.github/scripts/package.json +++ b/.github/scripts/package.json @@ -15,7 +15,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "axios": "^1.7.4", + "axios": "^1.13.5", "simple-git": "^3.25.0" }, "devDependencies": { diff --git a/.github/scripts/yarn.lock b/.github/scripts/yarn.lock index 30c7858598d..6fa2709c910 100644 --- a/.github/scripts/yarn.lock +++ b/.github/scripts/yarn.lock @@ -151,7 +151,7 @@ __metadata: "@lavamoat/allow-scripts": "npm:^3.2.0" "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@types/node": "npm:^20.16.2" - axios: "npm:^1.7.4" + axios: "npm:^1.13.5" simple-git: "npm:^3.25.0" ts-node: "npm:^10.5.0" typescript: "npm:~5.4.5" @@ -487,6 +487,20 @@ __metadata: languageName: node linkType: hard +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -494,14 +508,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.7.4": - version: 1.7.5 - resolution: "axios@npm:1.7.5" +"axios@npm:^1.13.5": + version: 1.13.5 + resolution: "axios@npm:1.13.5" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.0" + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" proxy-from-env: "npm:^1.1.0" - checksum: 10/6cbcfe943a84089f420a900a3a3aeb54ee94dcc9c2b81b150434896357be5d1079eff0b1bbb628597371e79f896b1bc5776df04184756ba99656ff31df9a75bf + checksum: 10/db726d09902565ef9a0632893530028310e2ec2b95b727114eca1b101450b00014133dfc3871cffc87983fb922bca7e4874d7e2826d1550a377a157cdf3f05b6 languageName: node linkType: hard @@ -560,6 +574,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -668,6 +692,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -712,6 +747,41 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10/86814bf8afbcd8966653f731415888019d4bc4aca6b6c354132a7a75bb87566751e320369654a101d23a91c87a85c79b178bcf40332839bd347aff437c4fb65f + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -726,13 +796,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.15.6": - version: 1.15.6 - resolution: "follow-redirects@npm:1.15.6" +"follow-redirects@npm:^1.15.11": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: debug: optional: true - checksum: 10/70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7 + checksum: 10/07372fd74b98c78cf4d417d68d41fdaa0be4dcacafffb9e67b1e3cf090bc4771515e65020651528faab238f10f9b9c0d9707d6c1574a6c0387c5de1042cde9ba languageName: node linkType: hard @@ -746,14 +816,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" +"form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10/7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd languageName: node linkType: hard @@ -782,6 +854,13 @@ __metadata: languageName: node linkType: hard +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805 + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -789,6 +868,37 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.6": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.4.5 resolution: "glob@npm:10.4.5" @@ -805,6 +915,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1 + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -812,6 +929,22 @@ __metadata: languageName: node linkType: hard +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10/c74c5f5ceee3c8a5b8bc37719840dc3749f5b0306d818974141dda2471a1a2ca6c8e46b9d6ac222c5345df7a901c9b6f350b1e6d62763fec877e26609a401bfe + languageName: node + linkType: hard + "hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" @@ -995,6 +1128,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd + languageName: node + linkType: hard + "mime-db@npm:1.52.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ed4b9e9dc..6a47a4adbf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [7.64.1] + +### Fixed + + - fix: check chainRanking against ALLOWED_BRIDGE_CHAIN_IDS (#25808) + ## [7.64.0] ### Added @@ -156,7 +162,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Android ANR bug (#25596) - fix(analytics): cp-7.63.1 correct capitalization in Deep link event name (#25599) -- feat(perps): sdk reconnect on native socket event (#25022) (#25573) +- feat(perps): sdk reconnect on native socket event (#25022, #25573) ## [7.63.0] @@ -10353,7 +10359,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.64.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.64.1...HEAD +[7.64.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.64.0...v7.64.1 [7.64.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.63.1...v7.64.0 [7.63.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.63.0...v7.63.1 [7.63.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.62.2...v7.63.0 diff --git a/android/app/build.gradle b/android/app/build.gradle index 9d46584e019..1b30a2c851a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -187,8 +187,8 @@ android { applicationId "io.metamask" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionName "7.64.0" - versionCode 3646 + versionName "7.64.1" + versionCode 3667 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/app/core/redux/slices/bridge/index.test.ts b/app/core/redux/slices/bridge/index.test.ts index 5745a235206..1166148e533 100644 --- a/app/core/redux/slices/bridge/index.test.ts +++ b/app/core/redux/slices/bridge/index.test.ts @@ -17,6 +17,7 @@ import reducer, { selectIsBridgeEnabledDest, selectIsSwapsLive, selectDestChainRanking, + selectSourceChainRanking, } from '.'; import { BridgeToken, @@ -531,25 +532,28 @@ describe('bridge slice', () => { }); it('returns false when bridge is not enabled as source for the chain', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ - 'eip155:1' - ].isActiveSrc = false; + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable it (chainRanking presence = enabled) + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); - const result = selectIsBridgeEnabledSource(mockState, '0x1'); + const result = selectIsBridgeEnabledSource( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); - it('returns undefined when chain is not in bridge config', () => { + it('returns false when chain is not in bridge config', () => { const result = selectIsBridgeEnabledSource( mockRootState as unknown as RootState, '0x999' as Hex, ); - expect(result).toBeUndefined(); + expect(result).toBe(false); }); }); @@ -597,6 +601,77 @@ describe('bridge slice', () => { }); }); + describe('selectSourceChainRanking', () => { + it('returns only supported and user-configured chains', () => { + const result = selectSourceChainRanking( + mockRootState as unknown as RootState, + ); + + // Should return chains that are both in ALLOWED_BRIDGE_CHAIN_IDS + // and in the user's configured networks + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + + // Ethereum (0x1) is allowed and configured in the mock state + expect(result.some((chain) => chain.chainId === 'eip155:1')).toBe(true); + }); + + it('filters out unsupported EVM chains from chainRanking', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported EVM Chain' }, + ]; + + const result = selectSourceChainRanking( + mockState as unknown as RootState, + ); + + expect(result.some((chain) => chain.chainId === 'eip155:99999')).toBe( + false, + ); + }); + + it('filters out unsupported non-EVM chains from chainRanking', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'cosmos:cosmoshub-4', name: 'Unsupported Cosmos Chain' }, + ]; + + const result = selectSourceChainRanking( + mockState as unknown as RootState, + ); + + expect( + result.some((chain) => chain.chainId === 'cosmos:cosmoshub-4'), + ).toBe(false); + }); + + it('filters out chains not in user-configured networks', () => { + const result = selectSourceChainRanking( + mockRootState as unknown as RootState, + ); + + // Optimism (0xa) is in chainRanking and ALLOWED_BRIDGE_CHAIN_IDS + // AND in the mock user's configured networks + const hasOptimism = result.some((chain) => chain.chainId === 'eip155:10'); + expect(hasOptimism).toBe(true); + + // Verify no chains appear that aren't in the user's configured networks + // The user only has Ethereum (0x1) and Optimism (0xa) configured as EVM networks + result.forEach((chain) => { + if (chain.chainId.startsWith('eip155:')) { + expect(['eip155:1', 'eip155:10']).toContain(chain.chainId); + } + }); + }); + }); + describe('selectDestChainRanking', () => { it('returns chainRanking from feature flags', () => { const result = selectDestChainRanking( @@ -622,16 +697,75 @@ describe('bridge slice', () => { expect(hasEthereum).toBe(true); }); - it('returns all chains without filtering (unlike selectSourceChainRanking)', () => { + it('returns all supported chains without filtering by user-configured networks', () => { const result = selectDestChainRanking( mockRootState as unknown as RootState, ); - // selectDestChainRanking should return all chains from feature flags + // selectDestChainRanking should return all supported chains from feature flags // This is the key difference from selectSourceChainRanking which filters // by user-configured networks expect(result.length).toBeGreaterThan(0); }); + + it('filters out unsupported EVM chains not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported Future Chain' }, + ]; + + const result = selectDestChainRanking(mockState as unknown as RootState); + + expect(result.some((chain) => chain.chainId === 'eip155:99999')).toBe( + false, + ); + expect(result.some((chain) => chain.chainId === 'eip155:1')).toBe(true); + }); + + it('filters out unsupported non-EVM chains not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'cosmos:cosmoshub-4', name: 'Unsupported Cosmos Chain' }, + ]; + + const result = selectDestChainRanking(mockState as unknown as RootState); + + expect( + result.some((chain) => chain.chainId === 'cosmos:cosmoshub-4'), + ).toBe(false); + expect( + result.some( + (chain) => + chain.chainId === 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + ), + ).toBe(true); + }); + }); + + describe('selectIsBridgeEnabledSource - ALLOWED_BRIDGE_CHAIN_IDS filtering', () => { + it('returns false for a chain in chainRanking but not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + // Add an unsupported chain to chainRanking + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported Future Chain' }, + ]; + + const result = selectIsBridgeEnabledSource( + mockState as unknown as RootState, + '0x1869F' as Hex, // hex for 99999 + ); + + expect(result).toBe(false); + }); }); describe('selectIsSwapsLive', () => { @@ -671,19 +805,24 @@ describe('bridge slice', () => { }); it('returns false when bridge is disabled for both source and destination', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ - 'eip155:1' - ].isActiveSrc = false; - // @ts-expect-error - Mock state has correct structure at runtime + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable source (chainRanking presence = enabled) + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); + // Disable destination via chains config // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ + ( + mockState as any + ).engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ 'eip155:1' ].isActiveDest = false; - const result = selectIsSwapsLive(mockState, '0x1'); + const result = selectIsSwapsLive( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); @@ -697,13 +836,24 @@ describe('bridge slice', () => { expect(result).toBeUndefined(); }); - it('returns false when support flag is disabled', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime + it('returns false when support flag is disabled and source is not in chainRanking', () => { + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable source + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); + // Disable destination via support flag // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.support = false; + ( + mockState as any + ).engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.support = + false; - const result = selectIsSwapsLive(mockState, '0x1'); + const result = selectIsSwapsLive( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); diff --git a/app/core/redux/slices/bridge/index.ts b/app/core/redux/slices/bridge/index.ts index 992bedd3fcc..44b3c8eab29 100644 --- a/app/core/redux/slices/bridge/index.ts +++ b/app/core/redux/slices/bridge/index.ts @@ -279,7 +279,26 @@ export const selectBridgeFeatureFlags = createSelector( ); /** - * Selector that returns the chainRanking from feature flags filtered by user-configured networks. + * Checks whether a CAIP chain ID from chainRanking is supported by this version of the client. + * This ensures that chains added to the remote chainRanking flag in the future + * won't be surfaced by older app versions that lack support for them. + */ +const isAllowedBridgeChainId = (caipChainId: string): boolean => { + if (caipChainId.startsWith('eip155:')) { + const hexChainId = formatChainIdToHex(caipChainId); + return ALLOWED_BRIDGE_CHAIN_IDS.includes( + hexChainId as AllowedBridgeChainIds, + ); + } + return ALLOWED_BRIDGE_CHAIN_IDS.includes( + caipChainId as AllowedBridgeChainIds, + ); +}; + +/** + * Selector that returns the chainRanking from feature flags filtered by: + * 1. Chains supported by this version of the client + * 2. User-configured networks * Used by NetworkPills in SOURCE mode to show all networks the user has added. */ export const selectSourceChainRanking = createSelector( @@ -297,6 +316,11 @@ export const selectSourceChainRanking = createSelector( return chainRanking.filter((chain) => { const { chainId } = chain; + // First, ensure this chain is supported by the current client version + if (!isAllowedBridgeChainId(chainId)) { + return false; + } + // For EVM chains (eip155:*), extract the hex chain ID and check if enabled if (chainId.startsWith('eip155:')) { const hexChainId = formatChainIdToHex(chainId); @@ -310,14 +334,17 @@ export const selectSourceChainRanking = createSelector( ); /** - * Selector that returns all chains from chainRanking (all bridge-supported networks). + * Selector that returns all chains from chainRanking that are supported by this + * version of the client. * Used by NetworkPills in DEST mode to show all available destination networks. */ export const selectDestChainRanking = createSelector( selectBridgeFeatureFlags, (bridgeFeatureFlags) => { const { chainRanking } = bridgeFeatureFlags; - return chainRanking ?? []; + return (chainRanking ?? []).filter((chain) => + isAllowedBridgeChainId(chain.chainId), + ); }, ); @@ -333,9 +360,9 @@ export const selectIsBridgeEnabledSourceFactory = createSelector( (bridgeFeatureFlags) => (chainId: Hex | CaipChainId) => { const caipChainId = formatChainIdToCaip(chainId); - return ( - bridgeFeatureFlags.support && - bridgeFeatureFlags.chains[caipChainId]?.isActiveSrc + return bridgeFeatureFlags.chainRanking?.some( + (chain) => + chain.chainId === caipChainId && isAllowedBridgeChainId(chain.chainId), ); }, ); diff --git a/bitrise.yml b/bitrise.yml index ed6250b7327..49485582692 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3469,16 +3469,16 @@ app: PROJECT_LOCATION_IOS: ios - opts: is_expand: false - VERSION_NAME: 7.64.0 + VERSION_NAME: 7.64.1 - opts: is_expand: false - VERSION_NUMBER: 3646 + VERSION_NUMBER: 3667 - opts: is_expand: false - FLASK_VERSION_NAME: 7.64.0 + FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3646 + FLASK_VERSION_NUMBER: 3667 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 6398f785825..2374bf09af8 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1319,7 +1319,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1385,7 +1385,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1454,7 +1454,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1518,7 +1518,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1684,7 +1684,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1751,7 +1751,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( "$(inherited)", diff --git a/metro.config.js b/metro.config.js index d8f6d610150..10aa003483a 100644 --- a/metro.config.js +++ b/metro.config.js @@ -93,29 +93,39 @@ module.exports = function (baseConfig) { buffer: '@craftzdog/react-native-buffer', 'node:buffer': '@craftzdog/react-native-buffer', }, - resolveRequest: isE2E - ? (context, moduleName, platform) => { - if (moduleName === '@sentry/react-native') { - return { - type: 'sourceFile', - filePath: path.resolve( - __dirname, - 'tests/module-mocking/sentry/react-native.ts', - ), - }; - } - if (moduleName === '@sentry/core') { - return { - type: 'sourceFile', - filePath: path.resolve( - __dirname, - 'tests/module-mocking/sentry/core.ts', - ), - }; - } - return context.resolveRequest(context, moduleName, platform); + resolveRequest: (context, moduleName, platform) => { + // Use axios browser build so Node-only deps (e.g. http2) are never pulled in + if ( + moduleName === 'axios' || + moduleName.includes('axios/dist/node/') + ) { + return { + filePath: require.resolve('axios/dist/browser/axios.cjs'), + type: 'sourceFile', + }; + } + if (isE2E) { + if (moduleName === '@sentry/react-native') { + return { + type: 'sourceFile', + filePath: path.resolve( + __dirname, + 'tests/module-mocking/sentry/react-native.ts', + ), + }; } - : defaultConfig.resolver.resolveRequest, + if (moduleName === '@sentry/core') { + return { + type: 'sourceFile', + filePath: path.resolve( + __dirname, + 'tests/module-mocking/sentry/core.ts', + ), + }; + } + } + return context.resolveRequest(context, moduleName, platform); + }, }, transformer: { babelTransformerPath: require.resolve('./metro.transform.js'), diff --git a/package.json b/package.json index c13745a14af..e05958b48d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask", - "version": "7.64.0", + "version": "7.64.1", "private": true, "scripts": { "install:foundryup": "yarn mm-foundryup", @@ -142,7 +142,7 @@ "d3-color": "3.1.0", "color-string": "^1.9.1", "tough-cookie": "4.1.3", - "axios": "^1.12.0", + "axios": "^1.13.5", "redux-persist-filesystem-storage/react-native-blob-util": "^0.19.9", "xmldom": "npm:@xmldom/xmldom@0.7.13", "@metamask/metamask-eth-abis": "3.1.1", @@ -349,7 +349,7 @@ "@walletconnect/utils": "^2.23.0", "@xmldom/xmldom": "^0.8.10", "asyncstorage-down": "4.2.0", - "axios": "^1.8.2", + "axios": "^1.13.5", "bignumber.js": "^9.0.1", "bitcoin-address-validation": "2.2.3", "bnjs4": "npm:bn.js@^4.12.0", diff --git a/yarn.lock b/yarn.lock index 0b5ab0c732e..41623b64b7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22021,14 +22021,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.12.0": - version: 1.12.2 - resolution: "axios@npm:1.12.2" +"axios@npm:^1.13.5": + version: 1.13.5 + resolution: "axios@npm:1.13.5" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.4" + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" proxy-from-env: "npm:^1.1.0" - checksum: 10/886a79770594eaad76493fecf90344b567bd956240609b5dcd09bd0afe8d3e6f1ad6d3257a93a483b6192b409d4b673d9515a34619e3e3ed1b2c0ec2a83b20ba + checksum: 10/db726d09902565ef9a0632893530028310e2ec2b95b727114eca1b101450b00014133dfc3871cffc87983fb922bca7e4874d7e2826d1550a377a157cdf3f05b6 languageName: node linkType: hard @@ -29301,13 +29301,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.6": - version: 1.15.9 - resolution: "follow-redirects@npm:1.15.9" +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.11": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: debug: optional: true - checksum: 10/e3ab42d1097e90d28b913903841e6779eb969b62a64706a3eb983e894a5db000fbd89296f45f08885a0e54cd558ef62e81be1165da9be25a6c44920da10f424c + checksum: 10/07372fd74b98c78cf4d417d68d41fdaa0be4dcacafffb9e67b1e3cf090bc4771515e65020651528faab238f10f9b9c0d9707d6c1574a6c0387c5de1042cde9ba languageName: node linkType: hard @@ -29437,7 +29437,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:4.0.4, form-data@npm:^4.0.0, form-data@npm:^4.0.4": +"form-data@npm:4.0.4": version: 4.0.4 resolution: "form-data@npm:4.0.4" dependencies: @@ -29463,6 +29463,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0, form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd + languageName: node + linkType: hard + "format-util@npm:^1.0.3": version: 1.0.5 resolution: "format-util@npm:1.0.5" @@ -34874,7 +34887,7 @@ __metadata: appwright: "patch:appwright@patch%3Aappwright@npm%253A0.1.45%23~/.yarn/patches/appwright-npm-0.1.45-f282bc1c1b.patch%3A%3Aversion=0.1.45&hash=3beae4#~/.yarn/patches/appwright-patch-685d6e06a0.patch" assert: "npm:^1.5.0" asyncstorage-down: "npm:4.2.0" - axios: "npm:^1.8.2" + axios: "npm:^1.13.5" babel-jest: "npm:^29.7.0" babel-loader: "npm:^9.1.3" babel-plugin-inline-import: "npm:^3.0.0"