diff --git a/package.json b/package.json index 04098328933f..a04f6e9b3bc7 100644 --- a/package.json +++ b/package.json @@ -256,7 +256,8 @@ "@ledgerhq/evm-tools/axios": "^0.28.0", "@ledgerhq/hw-app-eth/axios": "^0.28.0", "@ledgerhq/hw-app-eth@npm:^6.39.0": "patch:@ledgerhq/hw-app-eth@npm%3A6.39.0#~/.yarn/patches/@ledgerhq-hw-app-eth-npm-6.39.0-866309bbbe.patch", - "@ledgerhq/evm-tools@npm:^1.2.3": "patch:@ledgerhq/evm-tools@npm%3A1.2.3#~/.yarn/patches/@ledgerhq-evm-tools-npm-1.2.3-414f44baa9.patch" + "@ledgerhq/evm-tools@npm:^1.2.3": "patch:@ledgerhq/evm-tools@npm%3A1.2.3#~/.yarn/patches/@ledgerhq-evm-tools-npm-1.2.3-414f44baa9.patch", + "cross-spawn@npm:^5.0.1": "^7.0.5" }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.25.9#~/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch", @@ -560,7 +561,7 @@ "concurrently": "^8.2.2", "copy-webpack-plugin": "^12.0.2", "core-js-pure": "^3.38.0", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.5", "crypto-browserify": "^3.12.0", "css-loader": "^6.10.0", "css-to-xpath": "^0.1.0", diff --git a/ui/components/app/modals/qr-scanner/qr-scanner.component.js b/ui/components/app/modals/qr-scanner/qr-scanner.component.js index 75e1a83417b9..51ae5a89a6ef 100644 --- a/ui/components/app/modals/qr-scanner/qr-scanner.component.js +++ b/ui/components/app/modals/qr-scanner/qr-scanner.component.js @@ -22,6 +22,10 @@ const READY_STATE = { READY: 'READY', }; +const ethereumPrefix = 'ethereum:'; +// A 0x-prefixed Ethereum address is 42 characters (2 prefix + 40 address) +const addressLength = 42; + const parseContent = (content) => { let type = 'unknown'; let values = {}; @@ -31,12 +35,18 @@ const parseContent = (content) => { // For ex. EIP-681 (https://eips.ethereum.org/EIPS/eip-681) // Ethereum address links - fox ex. ethereum:0x.....1111 - if (content.split('ethereum:').length > 1) { + if ( + content.split(ethereumPrefix).length > 1 && + content.length === ethereumPrefix.length + addressLength + ) { type = 'address'; - // uses regex capture groups to match and extract address while ignoring everything else + // uses regex capture groups to match and extract address values = { address: parseScanContent(content) }; // Regular ethereum addresses - fox ex. 0x.....1111 - } else if (content.substring(0, 2).toLowerCase() === '0x') { + } else if ( + content.substring(0, 2).toLowerCase() === '0x' && + content.length === addressLength + ) { type = 'address'; values = { address: content }; } diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index aad920b6cb8b..67d272b7642b 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -2196,6 +2196,58 @@ export const getAllEnabledNetworks = createDeepEqualSelector( ), ); +export const getChainIdsToPoll = createDeepEqualSelector( + getPreferences, + getNetworkConfigurationsByChainId, + getCurrentChainId, + (preferences, networkConfigurations, currentChainId) => { + const { pausedChainIds = [] } = preferences; + + if (!process.env.PORTFOLIO_VIEW) { + return [currentChainId]; + } + + return Object.keys(networkConfigurations).filter( + (chainId) => + !TEST_CHAINS.includes(chainId) && !pausedChainIds.includes(chainId), + ); + }, +); + +export const getNetworkClientIdsToPoll = createDeepEqualSelector( + getPreferences, + getNetworkConfigurationsByChainId, + getCurrentChainId, + (preferences, networkConfigurations, currentChainId) => { + const { pausedChainIds = [] } = preferences; + + if (!process.env.PORTFOLIO_VIEW) { + const networkConfiguration = networkConfigurations[currentChainId]; + return [ + networkConfiguration.rpcEndpoints[ + networkConfiguration.defaultRpcEndpointIndex + ].networkClientId, + ]; + } + + return Object.entries(networkConfigurations).reduce( + (acc, [chainId, network]) => { + if ( + !TEST_CHAINS.includes(chainId) && + !pausedChainIds.includes(chainId) + ) { + acc.push( + network.rpcEndpoints[network.defaultRpcEndpointIndex] + .networkClientId, + ); + } + return acc; + }, + [], + ); + }, +); + /** * To retrieve the maxBaseFee and priorityFee the user has set as default * diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index d6656e481709..85180dec45f4 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -838,6 +838,123 @@ describe('Selectors', () => { }); }); + describe('#getChainIdsToPoll', () => { + const networkConfigurationsByChainId = { + [CHAIN_IDS.MAINNET]: { + chainId: CHAIN_IDS.MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'mainnet' }], + }, + [CHAIN_IDS.LINEA_MAINNET]: { + chainId: CHAIN_IDS.LINEA_MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'linea-mainnet' }], + }, + [CHAIN_IDS.SEPOLIA]: { + chainId: CHAIN_IDS.SEPOLIA, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'sepolia' }], + }, + [CHAIN_IDS.LINEA_SEPOLIA]: { + chainId: CHAIN_IDS.LINEA_SEPOLIA, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'linea-sepolia' }], + }, + }; + + beforeEach(() => { + process.env.PORTFOLIO_VIEW = 'true'; + }); + + afterEach(() => { + process.env.PORTFOLIO_VIEW = undefined; + }); + + it('returns only non-test chain IDs', () => { + const chainIds = selectors.getChainIdsToPoll({ + metamask: { + preferences: { pausedChainIds: [] }, + networkConfigurationsByChainId, + selectedNetworkClientId: 'mainnet', + }, + }); + expect(Object.values(chainIds)).toHaveLength(2); + expect(chainIds).toStrictEqual([ + CHAIN_IDS.MAINNET, + CHAIN_IDS.LINEA_MAINNET, + ]); + }); + + it('does not return paused chain IDs', () => { + const chainIds = selectors.getChainIdsToPoll({ + metamask: { + preferences: { pausedChainIds: [CHAIN_IDS.LINEA_MAINNET] }, + networkConfigurationsByChainId, + selectedNetworkClientId: 'mainnet', + }, + }); + expect(Object.values(chainIds)).toHaveLength(1); + expect(chainIds).toStrictEqual([CHAIN_IDS.MAINNET]); + }); + }); + + describe('#getNetworkClientIdsToPoll', () => { + const networkConfigurationsByChainId = { + [CHAIN_IDS.MAINNET]: { + chainId: CHAIN_IDS.MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'mainnet' }], + }, + [CHAIN_IDS.LINEA_MAINNET]: { + chainId: CHAIN_IDS.LINEA_MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'linea-mainnet' }], + }, + [CHAIN_IDS.SEPOLIA]: { + chainId: CHAIN_IDS.SEPOLIA, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'sepolia' }], + }, + [CHAIN_IDS.LINEA_SEPOLIA]: { + chainId: CHAIN_IDS.LINEA_SEPOLIA, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 'linea-sepolia' }], + }, + }; + + beforeEach(() => { + process.env.PORTFOLIO_VIEW = 'true'; + }); + + afterEach(() => { + process.env.PORTFOLIO_VIEW = undefined; + }); + + it('returns only non-test chain IDs', () => { + const chainIds = selectors.getNetworkClientIdsToPoll({ + metamask: { + preferences: { pausedChainIds: [] }, + networkConfigurationsByChainId, + selectedNetworkClientId: 'mainnet', + }, + }); + expect(Object.values(chainIds)).toHaveLength(2); + expect(chainIds).toStrictEqual(['mainnet', 'linea-mainnet']); + }); + + it('does not return paused chain IDs', () => { + const chainIds = selectors.getNetworkClientIdsToPoll({ + metamask: { + preferences: { pausedChainIds: [CHAIN_IDS.LINEA_MAINNET] }, + networkConfigurationsByChainId, + selectedNetworkClientId: 'mainnet', + }, + }); + expect(Object.values(chainIds)).toHaveLength(1); + expect(chainIds).toStrictEqual(['mainnet']); + }); + }); + describe('#isHardwareWallet', () => { it('returns false if it is not a HW wallet', () => { const mockStateWithImported = modifyStateWithHWKeyring( diff --git a/yarn.lock b/yarn.lock index 4602bb34f62f..4e745717f247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16162,25 +16162,14 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^5.0.1": - version: 5.1.0 - resolution: "cross-spawn@npm:5.1.0" - dependencies: - lru-cache: "npm:^4.0.1" - shebang-command: "npm:^1.2.0" - which: "npm:^1.2.9" - checksum: 10/726939c9954fc70c20e538923feaaa33bebc253247d13021737c3c7f68cdc3e0a57f720c0fe75057c0387995349f3f12e20e9bfdbf12274db28019c7ea4ec166 - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" dependencies: path-key: "npm:^3.1.0" shebang-command: "npm:^2.0.0" which: "npm:^2.0.1" - checksum: 10/e1a13869d2f57d974de0d9ef7acbf69dc6937db20b918525a01dacb5032129bd552d290d886d981e99f1b624cb03657084cc87bd40f115c07ecf376821c729ce + checksum: 10/0d52657d7ae36eb130999dffff1168ec348687b48dd38e2ff59992ed916c88d328cf1d07ff4a4a10bc78de5e1c23f04b306d569e42f7a2293915c081e4dfee86 languageName: node linkType: hard @@ -26031,16 +26020,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^4.0.1": - version: 4.1.1 - resolution: "lru-cache@npm:4.1.1" - dependencies: - pseudomap: "npm:^1.0.2" - yallist: "npm:^2.1.2" - checksum: 10/a412db13e89abe202c2314e633bd8580be2a668ba2036c34da376ac66163aa9fba4727ca66ff7907ad68fb574511f0bc6275c0598fdaeeab92e1125f5397d0e4 - languageName: node - linkType: hard - "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -26927,7 +26906,7 @@ __metadata: copy-to-clipboard: "npm:^3.3.3" copy-webpack-plugin: "npm:^12.0.2" core-js-pure: "npm:^3.38.0" - cross-spawn: "npm:^7.0.3" + cross-spawn: "npm:^7.0.5" crypto-browserify: "npm:^3.12.0" css-loader: "npm:^6.10.0" css-to-xpath: "npm:^0.1.0" @@ -30655,13 +30634,6 @@ __metadata: languageName: node linkType: hard -"pseudomap@npm:^1.0.2": - version: 1.0.2 - resolution: "pseudomap@npm:1.0.2" - checksum: 10/856c0aae0ff2ad60881168334448e898ad7a0e45fe7386d114b150084254c01e200c957cf378378025df4e052c7890c5bd933939b0e0d2ecfcc1dc2f0b2991f5 - languageName: node - linkType: hard - "psl@npm:^1.1.33": version: 1.9.0 resolution: "psl@npm:1.9.0" @@ -33737,15 +33709,6 @@ __metadata: languageName: node linkType: hard -"shebang-command@npm:^1.2.0": - version: 1.2.0 - resolution: "shebang-command@npm:1.2.0" - dependencies: - shebang-regex: "npm:^1.0.0" - checksum: 10/9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -33755,13 +33718,6 @@ __metadata: languageName: node linkType: hard -"shebang-regex@npm:^1.0.0": - version: 1.0.0 - resolution: "shebang-regex@npm:1.0.0" - checksum: 10/404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 - languageName: node - linkType: hard - "shebang-regex@npm:^3.0.0": version: 3.0.0 resolution: "shebang-regex@npm:3.0.0" @@ -37911,7 +37867,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^1.2.12, which@npm:^1.2.14, which@npm:^1.2.9, which@npm:^1.3.1": +"which@npm:^1.2.12, which@npm:^1.2.14, which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" dependencies: @@ -38245,13 +38201,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^2.1.2": - version: 2.1.2 - resolution: "yallist@npm:2.1.2" - checksum: 10/75fc7bee4821f52d1c6e6021b91b3e079276f1a9ce0ad58da3c76b79a7e47d6f276d35e206a96ac16c1cf48daee38a8bb3af0b1522a3d11c8ffe18f898828832 - languageName: node - linkType: hard - "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1"