From 91c182215f15673407c666e3f6a6f1fe7d01af1c Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Mon, 3 Jun 2024 12:47:19 -0500 Subject: [PATCH] fix: `wallet_addEthereumChain` validation: `iconUrls` should be an expected param (#24920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** In https://github.com/MetaMask/metamask-extension/pull/24415, I introduced a bug where if an `iconUrls` property is included in a `wallet_addEthereumChain` request an error is thrown suggesting that this property is not expected. Though this property is not actually used it is in the spec as an optional (and thus valid) param for the `wallet_addEthereumChain` method and we may eventually leverage it. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/24920?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/2581 ## **Manual testing steps** 1. Go to https://docs.metamask.io/wallet/reference/wallet_addethereumchain/ 2. A `wallet_addEthereumChain` request is already formatted including an `iconUrls` param 3. Click the `Send Request` button 4. You should see an add ethereum chain confirmation pop up 5. Confirm the request and verify that the chain is added to the wallet. ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.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-extension/blob/develop/.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: jiexi Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> --- .../handlers/add-ethereum-chain.test.js | 43 +++++++++++++++++++ .../handlers/ethereum-chain-utils.js | 11 +++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index ea8193840e23..ee8993ceb3fc 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -91,6 +91,7 @@ describe('addEthereumChainHandler', () => { decimals: 18, }, blockExplorerUrls: ['https://optimistic.etherscan.io'], + iconUrls: ['https://optimism.icon.com'], }, ], }, @@ -424,6 +425,47 @@ describe('addEthereumChainHandler', () => { }); }); }); + + it('should return an error if an unexpected parameter is provided', async () => { + const mocks = makeMocks({ + permissionsFeatureFlagIsActive: false, + }); + const mockEnd = jest.fn(); + + const unexpectedParam = 'unexpected'; + + await addEthereumChainHandler( + { + origin: 'example.com', + params: [ + { + chainId: createMockNonInfuraConfiguration().chainId, + chainName: createMockNonInfuraConfiguration().nickname, + rpcUrls: [createMockNonInfuraConfiguration().rpcUrl], + nativeCurrency: { + symbol: createMockNonInfuraConfiguration().ticker, + decimals: 18, + }, + blockExplorerUrls: [ + createMockNonInfuraConfiguration().rpcPrefs.blockExplorerUrl, + ], + [unexpectedParam]: 'parameter', + }, + ], + }, + {}, + jest.fn(), + mockEnd, + mocks, + ); + + expect(mockEnd).toHaveBeenCalledWith( + ethErrors.rpc.invalidParams({ + message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, + }), + ); + }); + it('should handle errors during the switch network permission request', async () => { const mockError = new Error('Permission request failed'); const mocks = makeMocks({ @@ -463,6 +505,7 @@ describe('addEthereumChainHandler', () => { expect(mockEnd).toHaveBeenCalledWith(mockError); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); }); + it('should return an error if nativeCurrency.symbol does not match an existing network with the same chainId', async () => { const mocks = makeMocks({ permissionedChainIds: [CHAIN_IDS.MAINNET], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 08c9a6cc8894..f32caf49a66c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -90,11 +90,14 @@ export function validateAddEthereumChainParams(params, end) { ...otherParams } = params; - if (Object.keys(otherParams).length > 0) { + const otherKeys = Object.keys(otherParams).filter( + // iconUrls is a valid optional but not currently used parameter + (v) => !['iconUrls'].includes(v), + ); + + if (otherKeys.length > 0) { throw ethErrors.rpc.invalidParams({ - message: `Received unexpected keys on object parameter. Unsupported keys:\n${Object.keys( - otherParams, - )}`, + message: `Received unexpected keys on object parameter. Unsupported keys:\n${otherKeys}`, }); }