From b7bf2acc34b605e97fdc49c35665080535b17acf Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 21 Jan 2025 13:49:59 -0800 Subject: [PATCH 01/14] remove approvalFlowId. replace isAddFlow with autoApprove --- .../handlers/add-ethereum-chain.js | 21 +--- .../handlers/add-ethereum-chain.test.js | 16 +-- .../handlers/ethereum-chain-utils.js | 32 +---- .../handlers/ethereum-chain-utils.test.ts | 114 +++++------------- .../handlers/switch-ethereum-chain.js | 2 +- .../handlers/switch-ethereum-chain.test.js | 1 - app/scripts/metamask-controller.js | 6 - 7 files changed, 47 insertions(+), 145 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 721fe6e82107..5086424678ae 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -18,8 +18,6 @@ const addEthereumChain = { getNetworkConfigurationByChainId: true, setActiveNetwork: true, requestUserApproval: true, - startApprovalFlow: true, - endApprovalFlow: true, getCurrentChainIdForDomain: true, getCaveat: true, requestPermittedChainsPermissionForOrigin: true, @@ -40,8 +38,6 @@ async function addEthereumChainHandler( getNetworkConfigurationByChainId, setActiveNetwork, requestUserApproval, - startApprovalFlow, - endApprovalFlow, getCurrentChainIdForDomain, getCaveat, requestPermittedChainsPermissionForOrigin, @@ -79,7 +75,6 @@ async function addEthereumChainHandler( ); } - let approvalFlowId; let updatedNetwork = existingNetwork; let rpcIndex = existingNetwork?.rpcEndpoints.findIndex(({ url }) => @@ -93,14 +88,14 @@ async function addEthereumChainHandler( : undefined; // If there's something to add or update - if ( + + const shouldAddOrUpdateNetwork = !existingNetwork || rpcIndex !== existingNetwork.defaultRpcEndpointIndex || (firstValidBlockExplorerUrl && - blockExplorerIndex !== existingNetwork.defaultBlockExplorerUrlIndex) - ) { - ({ id: approvalFlowId } = await startApprovalFlow()); + blockExplorerIndex !== existingNetwork.defaultBlockExplorerUrlIndex); + if (shouldAddOrUpdateNetwork) { try { await requestUserApproval({ origin, @@ -183,7 +178,6 @@ async function addEthereumChainHandler( }); } } catch (error) { - endApprovalFlow({ id: approvalFlowId }); return end(error); } } @@ -193,16 +187,13 @@ async function addEthereumChainHandler( const { networkClientId } = updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - return switchChain(res, end, chainId, networkClientId, approvalFlowId, { - isAddFlow: true, + return switchChain(res, end, chainId, networkClientId, { + autoApprove: shouldAddOrUpdateNetwork, setActiveNetwork, getCaveat, - endApprovalFlow, requestPermittedChainsPermissionForOrigin, requestPermittedChainsPermissionIncrementalForOrigin, }); - } else if (approvalFlowId) { - endApprovalFlow({ id: approvalFlowId }); } res.result = null; 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 517921570540..158f7487d5a4 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 @@ -69,8 +69,6 @@ const createMockedHandler = () => { setActiveNetwork: jest.fn(), requestUserApproval: jest.fn().mockResolvedValue(123), getCaveat: jest.fn(), - startApprovalFlow: () => ({ id: 'approvalFlowId' }), - endApprovalFlow: jest.fn(), addNetwork: jest.fn().mockResolvedValue({ defaultRpcEndpointIndex: 0, rpcEndpoints: [{ networkClientId: 123 }], @@ -185,10 +183,8 @@ describe('addEthereumChainHandler', () => { end, NON_INFURA_CHAIN_ID, 123, - 'approvalFlowId', { - isAddFlow: true, - endApprovalFlow: mocks.endApprovalFlow, + autoApprove: true, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: @@ -254,10 +250,8 @@ describe('addEthereumChainHandler', () => { end, '0x1', 123, - 'approvalFlowId', { - isAddFlow: true, - endApprovalFlow: mocks.endApprovalFlow, + autoApprove: true, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: @@ -270,7 +264,7 @@ describe('addEthereumChainHandler', () => { }); describe('if the proposed networkConfiguration does not have a different rpcUrl from the one already in state', () => { - it('should only switch to the existing networkConfiguration if one already exists for the given chain id', async () => { + it('should only switch to the existing networkConfiguration if one already exists for the given chain id without auto approving the chain permission', async () => { const { mocks, end, handler } = createMockedHandler(); mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); mocks.getNetworkConfigurationByChainId.mockReturnValue( @@ -303,10 +297,8 @@ describe('addEthereumChainHandler', () => { end, '0xa', createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, - undefined, { - isAddFlow: true, - endApprovalFlow: mocks.endApprovalFlow, + autoApprove: false, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: 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 d4b62576df63..0f44959f9af1 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 @@ -1,4 +1,4 @@ -import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; +import { rpcErrors } from '@metamask/rpc-errors'; import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -164,26 +164,22 @@ export function validateAddEthereumChainParams(params) { * @param end - The JSON RPC request's end callback. * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. - * @param {string} [approvalFlowId] - The optional approval flow ID to handle. * @param {object} hooks - The hooks object. - * @param {boolean} hooks.isAddFlow - The boolean determining if this call originates from wallet_addEthereumChain. * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. - * @param {Function} hooks.endApprovalFlow - The optional callback to end the approval flow when approvalFlowId is provided. * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. * @param {Function} hooks.requestPermittedChainsPermissionForOrigin - The callback to request a new permittedChains-equivalent CAIP-25 permission. * @param {Function} hooks.requestPermittedChainsPermissionIncrementalForOrigin - The callback to add a new chain to the permittedChains-equivalent CAIP-25 permission. - * @returns a null response on success or an error if user rejects an approval when isAddFlow is false or on unexpected errors. + * @param hooks.autoApprove + * @returns a null response on success or an error if user rejects an approval when autoApprove is false or on unexpected errors. */ export async function switchChain( response, end, chainId, networkClientId, - approvalFlowId, { - isAddFlow, + autoApprove, setActiveNetwork, - endApprovalFlow, getCaveat, requestPermittedChainsPermissionForOrigin, requestPermittedChainsPermissionIncrementalForOrigin, @@ -201,36 +197,20 @@ export async function switchChain( if (!ethChainIds.includes(chainId)) { await requestPermittedChainsPermissionIncrementalForOrigin({ chainId, - autoApprove: isAddFlow, + autoApprove, }); } } else { await requestPermittedChainsPermissionForOrigin({ chainId, - autoApprove: isAddFlow, + autoApprove, }); } await setActiveNetwork(networkClientId); response.result = null; } catch (error) { - // We don't want to return an error if user rejects the request - // and this is a chained switch request after wallet_addEthereumChain. - // approvalFlowId is only defined when this call is of a - // wallet_addEthereumChain request so we can use it to determine - // if we should return an error - if ( - error.code === errorCodes.provider.userRejectedRequest && - approvalFlowId - ) { - response.result = null; - return end(); - } return end(error); - } finally { - if (approvalFlowId) { - endApprovalFlow({ id: approvalFlowId }); - } } return end(); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index 51b77372dd09..e699337106c3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -10,27 +10,15 @@ describe('Ethereum Chain Utils', () => { const createMockedSwitchChain = () => { const end = jest.fn(); const mocks = { - isAddFlow: false, + autoApprove: false, setActiveNetwork: jest.fn(), - endApprovalFlow: jest.fn(), getCaveat: jest.fn(), requestPermittedChainsPermissionForOrigin: jest.fn(), requestPermittedChainsPermissionIncrementalForOrigin: jest.fn(), }; const response: { result?: true } = {}; - const switchChain = ( - chainId: Hex, - networkClientId: string, - approvalFlowId?: string, - ) => - EthChainUtils.switchChain( - response, - end, - chainId, - networkClientId, - approvalFlowId, - mocks, - ); + const switchChain = (chainId: Hex, networkClientId: string) => + EthChainUtils.switchChain(response, end, chainId, networkClientId, mocks); return { mocks, @@ -43,7 +31,7 @@ describe('Ethereum Chain Utils', () => { describe('switchChain', () => { it('gets the CAIP-25 caveat', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect(mocks.getCaveat).toHaveBeenCalledWith({ target: Caip25EndowmentPermissionName, @@ -51,55 +39,11 @@ describe('Ethereum Chain Utils', () => { }); }); - it('passes through unexpected errors if approvalFlowId is not provided', async () => { - const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( - new Error('unexpected error'), - ); - - await switchChain('0x1', 'mainnet', undefined); - - expect(end).toHaveBeenCalledWith(new Error('unexpected error')); - }); - - it('passes through unexpected errors if approvalFlowId is provided', async () => { - const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( - new Error('unexpected error'), - ); - - await switchChain('0x1', 'mainnet', 'approvalFlowId'); - - expect(end).toHaveBeenCalledWith(new Error('unexpected error')); - }); - - it('ignores userRejectedRequest errors when approvalFlowId is provided', async () => { - const { mocks, end, response, switchChain } = createMockedSwitchChain(); - mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce({ - code: errorCodes.provider.userRejectedRequest, - }); - - await switchChain('0x1', 'mainnet', 'approvalFlowId'); - - expect(response.result).toStrictEqual(null); - expect(end).toHaveBeenCalledWith(); - }); - - it('ends the approval flow when approvalFlowId is provided', async () => { - const { mocks, switchChain } = createMockedSwitchChain(); - - await switchChain('0x1', 'mainnet', 'approvalFlowId'); - - expect(mocks.endApprovalFlow).toHaveBeenCalledWith({ - id: 'approvalFlowId', - }); - }); - describe('with no existing CAIP-25 permission', () => { - it('requests a switch chain approval without autoApprove if isAddFlow: false', async () => { + it('requests a switch chain approval without autoApprove if autoApprove: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - mocks.isAddFlow = false; - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + mocks.autoApprove = false; + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionForOrigin, @@ -108,31 +52,33 @@ describe('Ethereum Chain Utils', () => { it('switches to the chain', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('should handle errors if the switch chain approval is rejected', async () => { + it('should throw an error if the switch chain approval is rejected', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce({ code: errorCodes.provider.userRejectedRequest, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionForOrigin, ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); - expect(end).toHaveBeenCalledWith(); + expect(end).toHaveBeenCalledWith({ + code: errorCodes.provider.userRejectedRequest, + }); }); }); describe('with an existing CAIP-25 permission granted from the legacy flow (isMultichainOrigin: false) and the chainId is not already permissioned', () => { - it('requests a switch chain approval with autoApprove and switches to it if isAddFlow: true', async () => { + it('requests a switch chain approval with autoApprove and switches to it if autoApprove: true', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - mocks.isAddFlow = true; + mocks.autoApprove = true; mocks.getCaveat.mockReturnValue({ value: { requiredScopes: {}, @@ -140,7 +86,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionIncrementalForOrigin, @@ -148,9 +94,9 @@ describe('Ethereum Chain Utils', () => { expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('requests permittedChains approval without autoApprove then switches to it if isAddFlow: false', async () => { + it('requests permittedChains approval without autoApprove then switches to it if autoApprove: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - mocks.isAddFlow = false; + mocks.autoApprove = false; mocks.getCaveat.mockReturnValue({ value: { requiredScopes: {}, @@ -158,7 +104,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionIncrementalForOrigin, @@ -166,12 +112,10 @@ describe('Ethereum Chain Utils', () => { expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('should handle errors if the permittedChains approval is rejected', async () => { + it('should throw errors if the permittedChains grant fails', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValueOnce( - { - code: errorCodes.provider.userRejectedRequest, - }, + new Error('failed to auto grant permittedChains'), ); mocks.getCaveat.mockReturnValue({ value: { @@ -180,13 +124,15 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionIncrementalForOrigin, ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); - expect(end).toHaveBeenCalledWith(); + expect(end).toHaveBeenCalledWith( + new Error('failed to auto grant permittedChains'), + ); }); }); @@ -205,7 +151,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionIncrementalForOrigin, @@ -227,7 +173,7 @@ describe('Ethereum Chain Utils', () => { ), ); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); }); @@ -247,7 +193,7 @@ describe('Ethereum Chain Utils', () => { ), ); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect(end).toHaveBeenCalledWith( new Error( @@ -277,7 +223,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect( mocks.requestPermittedChainsPermissionIncrementalForOrigin, @@ -297,7 +243,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin, }, }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet'); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js index 4a5e0ef6a4f8..9bed994ed33a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js @@ -64,7 +64,7 @@ async function switchEthereumChainHandler( ); } - return switchChain(res, end, chainId, networkClientIdToSwitchTo, null, { + return switchChain(res, end, chainId, networkClientIdToSwitchTo, { setActiveNetwork, getCaveat, requestPermittedChainsPermissionForOrigin, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 694839b4562b..fedf456eb61c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -164,7 +164,6 @@ describe('switchEthereumChainHandler', () => { end, '0xdeadbeef', 'mainnet', - null, { setActiveNetwork: mocks.setActiveNetwork, getCaveat: mocks.getCaveat, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b307cda155ab..df6aaedd182b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -6343,12 +6343,6 @@ export default class MetamaskController extends EventEmitter { this.approvalController.addAndShowApprovalRequest.bind( this.approvalController, ), - startApprovalFlow: this.approvalController.startFlow.bind( - this.approvalController, - ), - endApprovalFlow: this.approvalController.endFlow.bind( - this.approvalController, - ), sendMetrics: this.metaMetricsController.trackEvent.bind( this.metaMetricsController, ), From 65a837b0f57296f6e001e3ec5baa0dd13728e59b Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 21 Jan 2025 13:50:24 -0800 Subject: [PATCH 02/14] WIP wallet_addEthereumChain specs --- test/e2e/json-rpc/wallet_addEthereumChain.ts | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/e2e/json-rpc/wallet_addEthereumChain.ts diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.ts b/test/e2e/json-rpc/wallet_addEthereumChain.ts new file mode 100644 index 000000000000..bc69e189a086 --- /dev/null +++ b/test/e2e/json-rpc/wallet_addEthereumChain.ts @@ -0,0 +1,36 @@ +const { strict: assert } = require('assert'); +const { + withFixtures, + defaultGanacheOptions, + openDapp, + DAPP_URL, + DAPP_ONE_URL, + unlockWallet, + switchToNotificationWindow, + WINDOW_TITLES, +} = require('../helpers'); +const FixtureBuilder = require('../fixture-builder'); +const { isManifestV3 } = require('../../../shared/modules/mv3.utils'); + +describe('Add Ethereum Chain', function () { + describe('the dapp is not already permitted to use the chain being added', () => { + it('automatically permits and switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { }); + + + it('automatically permits and switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { + }); + + it('prompts to switch to the chain when the rpc endpoint being added already exists', async function () { + }); + }) + + describe('the dapp is already permitted to use the chain being added', () => { + it('automatically switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { }); + + it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { + }); + + it('automatically switches to the chain when the rpc endpoint being added already exists for the chain', async function () { + }); + }) +}); From 15248fbfeade46e029ed254d58e495d176d26dc5 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 21 Jan 2025 15:08:39 -0800 Subject: [PATCH 03/14] replace withPermissionControllerConnectedToTestDappWithChain with withPermissionControllerConnectedToTestDappWithChains --- test/e2e/fixture-builder.js | 40 ++++++++++++++++++- .../json-rpc/wallet_revokePermissions.spec.ts | 6 +-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 9e83c8f2db0b..9fa70c624df6 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -490,7 +490,7 @@ class FixtureBuilder { }); } - withPermissionControllerConnectedToTestDappWithChain() { + withPermissionControllerConnectedToTestDappWithChains(chainIds) { return this.withPermissionController({ subjects: { [DAPP_URL]: { @@ -515,7 +515,43 @@ class FixtureBuilder { caveats: [ { type: 'restrictNetworkSwitching', - value: ['0x539'], + value: chainIds, + }, + ], + date: 1664388714637, + }, + }, + }, + }, + }); + } + + withPermissionControllerConnectedToTestDappWithTwoChains() { + return this.withPermissionController({ + subjects: { + [DAPP_URL]: { + origin: DAPP_URL, + permissions: { + eth_accounts: { + id: 'ZaqPEWxyhNCJYACFw93jE', + parentCapability: 'eth_accounts', + invoker: DAPP_URL, + caveats: [ + { + type: 'restrictReturnedAccounts', + value: [DEFAULT_FIXTURE_ACCOUNT.toLowerCase()], + }, + ], + date: 1664388714636, + }, + 'endowment:permitted-chains': { + id: 'D7cac0a2e3BD8f349506a', + parentCapability: 'endowment:permitted-chains', + invoker: DAPP_URL, + caveats: [ + { + type: 'restrictNetworkSwitching', + value: ['0x539', '0x53a'], }, ], date: 1664388714637, diff --git a/test/e2e/json-rpc/wallet_revokePermissions.spec.ts b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts index d1ac0d39f580..336e685c407a 100644 --- a/test/e2e/json-rpc/wallet_revokePermissions.spec.ts +++ b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts @@ -11,7 +11,7 @@ describe('Revoke Dapp Permissions', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDappWithChain() + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) .build(), title: this.test?.fullTitle(), }, @@ -69,7 +69,7 @@ describe('Revoke Dapp Permissions', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDappWithChain() + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) .build(), title: this.test?.fullTitle(), }, @@ -127,7 +127,7 @@ describe('Revoke Dapp Permissions', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDappWithChain() + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) .build(), title: this.test?.fullTitle(), }, From 0d44554499e6b2636b6854d8f673b5f01735f390 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 21 Jan 2025 15:08:48 -0800 Subject: [PATCH 04/14] add wallet_addEthereumChain spec --- .../json-rpc/wallet_addEthereumChain.spec.ts | 308 ++++++++++++++++++ test/e2e/json-rpc/wallet_addEthereumChain.ts | 36 -- 2 files changed, 308 insertions(+), 36 deletions(-) create mode 100644 test/e2e/json-rpc/wallet_addEthereumChain.spec.ts delete mode 100644 test/e2e/json-rpc/wallet_addEthereumChain.ts diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts new file mode 100644 index 000000000000..fd486a6d9536 --- /dev/null +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -0,0 +1,308 @@ +import { strict as assert } from 'assert'; +import FixtureBuilder from '../fixture-builder'; +import { Driver } from '../webdriver/driver'; +import { + withFixtures, + defaultGanacheOptions, + openDapp, + DAPP_URL, + DAPP_ONE_URL, + unlockWallet, + switchToNotificationWindow, + WINDOW_TITLES, +} from '../helpers'; +import { PermissionNames } from '../../../app/scripts/controllers/permissions'; +import { CaveatTypes } from '../../../shared/constants/permissions'; +import { CaveatConstraint, PermissionConstraint } from '@metamask/permission-controller'; + + +const getPermittedChains = async (driver) => { + const getPermissionsRequest = JSON.stringify({ + method: 'wallet_getPermissions', + }); + const getPermissionsResult = await driver.executeScript( + `return window.ethereum.request(${getPermissionsRequest})`, + ); + + const permittedChains = + getPermissionsResult + ?.find( + (permission: PermissionConstraint) => + permission.parentCapability === PermissionNames.permittedChains, + ) + ?.caveats.find( + (caveat: CaveatConstraint) => caveat.type === CaveatTypes.restrictNetworkSwitching, + )?.value || []; + + return permittedChains +} + +describe('Add Ethereum Chain', function () { + describe('the dapp is not already permitted to use the chain being added', () => { + it('automatically permits and switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port: 8546, chainId: 1338 }], + }, + title: this.test?.fullTitle(), + }, + async ({ driver }: {driver: Driver}) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver) + + assert.deepEqual(beforePermittedChains, []) + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [{ + chainId: "0x53a", + chainName: "Localhost 1338", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + rpcUrls: ["http://localhost:8546"], + blockExplorerUrls: [] + }] + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.clickElement({ text: 'Approve', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver) + assert.deepEqual(afterPermittedChains, ['0x53a']) + }, + ); + }); + + it('automatically permits and switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache() + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port: 8546, chainId: 1338 }], + }, + title: this.test?.fullTitle(), + }, + async ({ driver }: {driver: Driver}) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver) + + assert.deepEqual(beforePermittedChains, []) + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [{ + chainId: "0x53a", + chainName: "Localhost 8546 alternative", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + // this does not match what already exists in the NetworkController + rpcUrls: ["http://127.0.0.1:8546"], + blockExplorerUrls: [] + }] + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.clickElement({ text: 'Approve', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver) + assert.deepEqual(afterPermittedChains, ['0x53a']) + }, + ); + }); + + it('prompts to switch to the chain when the rpc endpoint being added already exists', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache() + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port: 8546, chainId: 1338 }], + }, + title: this.test?.fullTitle(), + }, + async ({ driver }: {driver: Driver}) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver) + + assert.deepEqual(beforePermittedChains, []) + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [{ + chainId: "0x53a", + chainName: "Localhost 8546", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + // this matches what already exists in the NetworkController + rpcUrls: ["http://localhost:8546"], + blockExplorerUrls: [] + }] + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver) + assert.deepEqual(afterPermittedChains, ['0x53a']) + }, + ); + }); + }) + + describe('the dapp is already permitted to use the chain being added', () => { + it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache().withPermissionControllerConnectedToTestDappWithChains(['0x539', '0x53a']) + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port: 8546, chainId: 1338 }], + }, + title: this.test?.fullTitle(), + }, + async ({ driver }: {driver: Driver}) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver) + assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']) + + // should start on 1337 + await driver.findElement({ css: '#chainId', text: '0x539' }); + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [{ + chainId: "0x53a", + chainName: "Localhost 8546 alternative", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + // this does not match what already exists in the NetworkController + rpcUrls: ["http://127.0.0.1:8546"], + blockExplorerUrls: [] + }] + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.clickElement({ text: 'Approve', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver) + assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']) + + // should end on 1338 + await driver.findElement({ css: '#chainId', text: '0x53a' }); + }, + ); + }); + + it('automatically switches to the chain when the rpc endpoint being added already exists for the chain', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache().withPermissionControllerConnectedToTestDappWithChains(['0x539', '0x53a']) + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port: 8546, chainId: 1338 }], + }, + title: this.test?.fullTitle(), + }, + async ({ driver }: {driver: Driver}) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver) + assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']) + + // should start on 1337 + await driver.findElement({ css: '#chainId', text: '0x539' }); + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [{ + chainId: "0x53a", + chainName: "Localhost 8546", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + // this matches what already exists in the NetworkController + rpcUrls: ["http://localhost:8546"], + blockExplorerUrls: [] + }] + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + const afterPermittedChains = await getPermittedChains(driver) + assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']) + + // should end on 1338 + await driver.findElement({ css: '#chainId', text: '0x53a' }); + }, + ); + }); + }) +}); diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.ts b/test/e2e/json-rpc/wallet_addEthereumChain.ts deleted file mode 100644 index bc69e189a086..000000000000 --- a/test/e2e/json-rpc/wallet_addEthereumChain.ts +++ /dev/null @@ -1,36 +0,0 @@ -const { strict: assert } = require('assert'); -const { - withFixtures, - defaultGanacheOptions, - openDapp, - DAPP_URL, - DAPP_ONE_URL, - unlockWallet, - switchToNotificationWindow, - WINDOW_TITLES, -} = require('../helpers'); -const FixtureBuilder = require('../fixture-builder'); -const { isManifestV3 } = require('../../../shared/modules/mv3.utils'); - -describe('Add Ethereum Chain', function () { - describe('the dapp is not already permitted to use the chain being added', () => { - it('automatically permits and switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { }); - - - it('automatically permits and switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { - }); - - it('prompts to switch to the chain when the rpc endpoint being added already exists', async function () { - }); - }) - - describe('the dapp is already permitted to use the chain being added', () => { - it('automatically switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { }); - - it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { - }); - - it('automatically switches to the chain when the rpc endpoint being added already exists for the chain', async function () { - }); - }) -}); From 493e644e12ee14d91b19390b6529e07823921030 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 21 Jan 2025 15:14:15 -0800 Subject: [PATCH 05/14] lint --- .../json-rpc/wallet_addEthereumChain.spec.ts | 221 ++++++++++-------- .../json-rpc/wallet_revokePermissions.spec.ts | 4 +- 2 files changed, 123 insertions(+), 102 deletions(-) diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index fd486a6d9536..b188310fc73e 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -1,22 +1,21 @@ import { strict as assert } from 'assert'; +import { + CaveatConstraint, + PermissionConstraint, +} from '@metamask/permission-controller'; import FixtureBuilder from '../fixture-builder'; import { Driver } from '../webdriver/driver'; import { withFixtures, defaultGanacheOptions, openDapp, - DAPP_URL, - DAPP_ONE_URL, unlockWallet, - switchToNotificationWindow, WINDOW_TITLES, } from '../helpers'; import { PermissionNames } from '../../../app/scripts/controllers/permissions'; import { CaveatTypes } from '../../../shared/constants/permissions'; -import { CaveatConstraint, PermissionConstraint } from '@metamask/permission-controller'; - -const getPermittedChains = async (driver) => { +const getPermittedChains = async (driver: Driver) => { const getPermissionsRequest = JSON.stringify({ method: 'wallet_getPermissions', }); @@ -31,11 +30,12 @@ const getPermittedChains = async (driver) => { permission.parentCapability === PermissionNames.permittedChains, ) ?.caveats.find( - (caveat: CaveatConstraint) => caveat.type === CaveatTypes.restrictNetworkSwitching, + (caveat: CaveatConstraint) => + caveat.type === CaveatTypes.restrictNetworkSwitching, )?.value || []; - return permittedChains -} + return permittedChains; +}; describe('Add Ethereum Chain', function () { describe('the dapp is not already permitted to use the chain being added', () => { @@ -43,49 +43,50 @@ describe('Add Ethereum Chain', function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder() - .build(), + fixtures: new FixtureBuilder().build(), ganacheOptions: { ...defaultGanacheOptions, concurrent: [{ port: 8546, chainId: 1338 }], }, title: this.test?.fullTitle(), }, - async ({ driver }: {driver: Driver}) => { + async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await openDapp(driver); - const beforePermittedChains = await getPermittedChains(driver) + const beforePermittedChains = await getPermittedChains(driver); - assert.deepEqual(beforePermittedChains, []) + assert.deepEqual(beforePermittedChains, []); const switchEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', - params: [{ - chainId: "0x53a", - chainName: "Localhost 1338", - nativeCurrency: { - name: "", - symbol: "ETH", - decimals: 18 + params: [ + { + chainId: '0x53a', + chainName: 'Localhost 1338', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: ['http://localhost:8546'], + blockExplorerUrls: [], }, - rpcUrls: ["http://localhost:8546"], - blockExplorerUrls: [] - }] + ], }); await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Approve', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - const afterPermittedChains = await getPermittedChains(driver) - assert.deepEqual(afterPermittedChains, ['0x53a']) + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x53a']); }, ); }); @@ -94,7 +95,8 @@ describe('Add Ethereum Chain', function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache() + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() .build(), ganacheOptions: { ...defaultGanacheOptions, @@ -102,42 +104,44 @@ describe('Add Ethereum Chain', function () { }, title: this.test?.fullTitle(), }, - async ({ driver }: {driver: Driver}) => { + async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await openDapp(driver); - const beforePermittedChains = await getPermittedChains(driver) + const beforePermittedChains = await getPermittedChains(driver); - assert.deepEqual(beforePermittedChains, []) + assert.deepEqual(beforePermittedChains, []); const switchEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', - params: [{ - chainId: "0x53a", - chainName: "Localhost 8546 alternative", - nativeCurrency: { - name: "", - symbol: "ETH", - decimals: 18 + params: [ + { + chainId: '0x53a', + chainName: 'Localhost 8546 alternative', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this does not match what already exists in the NetworkController + rpcUrls: ['http://127.0.0.1:8546'], + blockExplorerUrls: [], }, - // this does not match what already exists in the NetworkController - rpcUrls: ["http://127.0.0.1:8546"], - blockExplorerUrls: [] - }] + ], }); await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Approve', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - const afterPermittedChains = await getPermittedChains(driver) - assert.deepEqual(afterPermittedChains, ['0x53a']) + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x53a']); }, ); }); @@ -146,7 +150,8 @@ describe('Add Ethereum Chain', function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache() + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() .build(), ganacheOptions: { ...defaultGanacheOptions, @@ -154,53 +159,60 @@ describe('Add Ethereum Chain', function () { }, title: this.test?.fullTitle(), }, - async ({ driver }: {driver: Driver}) => { + async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await openDapp(driver); - const beforePermittedChains = await getPermittedChains(driver) + const beforePermittedChains = await getPermittedChains(driver); - assert.deepEqual(beforePermittedChains, []) + assert.deepEqual(beforePermittedChains, []); const switchEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', - params: [{ - chainId: "0x53a", - chainName: "Localhost 8546", - nativeCurrency: { - name: "", - symbol: "ETH", - decimals: 18 + params: [ + { + chainId: '0x53a', + chainName: 'Localhost 8546', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this matches what already exists in the NetworkController + rpcUrls: ['http://localhost:8546'], + blockExplorerUrls: [], }, - // this matches what already exists in the NetworkController - rpcUrls: ["http://localhost:8546"], - blockExplorerUrls: [] - }] + ], }); await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Confirm', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - const afterPermittedChains = await getPermittedChains(driver) - assert.deepEqual(afterPermittedChains, ['0x53a']) + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x53a']); }, ); }); - }) + }); describe('the dapp is already permitted to use the chain being added', () => { it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache().withPermissionControllerConnectedToTestDappWithChains(['0x539', '0x53a']) + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() + .withPermissionControllerConnectedToTestDappWithChains([ + '0x539', + '0x53a', + ]) .build(), ganacheOptions: { ...defaultGanacheOptions, @@ -208,12 +220,12 @@ describe('Add Ethereum Chain', function () { }, title: this.test?.fullTitle(), }, - async ({ driver }: {driver: Driver}) => { + async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await openDapp(driver); - const beforePermittedChains = await getPermittedChains(driver) - assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']) + const beforePermittedChains = await getPermittedChains(driver); + assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']); // should start on 1337 await driver.findElement({ css: '#chainId', text: '0x539' }); @@ -221,31 +233,33 @@ describe('Add Ethereum Chain', function () { const switchEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', - params: [{ - chainId: "0x53a", - chainName: "Localhost 8546 alternative", - nativeCurrency: { - name: "", - symbol: "ETH", - decimals: 18 + params: [ + { + chainId: '0x53a', + chainName: 'Localhost 8546 alternative', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this does not match what already exists in the NetworkController + rpcUrls: ['http://127.0.0.1:8546'], + blockExplorerUrls: [], }, - // this does not match what already exists in the NetworkController - rpcUrls: ["http://127.0.0.1:8546"], - blockExplorerUrls: [] - }] + ], }); await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog) + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Approve', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - const afterPermittedChains = await getPermittedChains(driver) - assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']) + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']); // should end on 1338 await driver.findElement({ css: '#chainId', text: '0x53a' }); @@ -257,7 +271,12 @@ describe('Add Ethereum Chain', function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder().withNetworkControllerDoubleGanache().withPermissionControllerConnectedToTestDappWithChains(['0x539', '0x53a']) + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() + .withPermissionControllerConnectedToTestDappWithChains([ + '0x539', + '0x53a', + ]) .build(), ganacheOptions: { ...defaultGanacheOptions, @@ -265,12 +284,12 @@ describe('Add Ethereum Chain', function () { }, title: this.test?.fullTitle(), }, - async ({ driver }: {driver: Driver}) => { + async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await openDapp(driver); - const beforePermittedChains = await getPermittedChains(driver) - assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']) + const beforePermittedChains = await getPermittedChains(driver); + assert.deepEqual(beforePermittedChains, ['0x539', '0x53a']); // should start on 1337 await driver.findElement({ css: '#chainId', text: '0x539' }); @@ -278,31 +297,33 @@ describe('Add Ethereum Chain', function () { const switchEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', - params: [{ - chainId: "0x53a", - chainName: "Localhost 8546", - nativeCurrency: { - name: "", - symbol: "ETH", - decimals: 18 + params: [ + { + chainId: '0x53a', + chainName: 'Localhost 8546', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this matches what already exists in the NetworkController + rpcUrls: ['http://localhost:8546'], + blockExplorerUrls: [], }, - // this matches what already exists in the NetworkController - rpcUrls: ["http://localhost:8546"], - blockExplorerUrls: [] - }] + ], }); await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - const afterPermittedChains = await getPermittedChains(driver) - assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']) + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x539', '0x53a']); // should end on 1338 await driver.findElement({ css: '#chainId', text: '0x53a' }); }, ); }); - }) + }); }); diff --git a/test/e2e/json-rpc/wallet_revokePermissions.spec.ts b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts index 336e685c407a..1f7758db3fc8 100644 --- a/test/e2e/json-rpc/wallet_revokePermissions.spec.ts +++ b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts @@ -69,7 +69,7 @@ describe('Revoke Dapp Permissions', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDappWithChains(['0x539']) + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) .build(), title: this.test?.fullTitle(), }, @@ -127,7 +127,7 @@ describe('Revoke Dapp Permissions', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDappWithChains(['0x539']) + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) .build(), title: this.test?.fullTitle(), }, From d21a6bca64b6eaf80f2453d6d6aae9b8ba2526c9 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 22 Jan 2025 09:54:32 -0800 Subject: [PATCH 06/14] remove withPermissionControllerConnectedToTestDappWithTwoChains --- test/e2e/fixture-builder.js | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 9fa70c624df6..d452f86065f1 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -526,42 +526,6 @@ class FixtureBuilder { }); } - withPermissionControllerConnectedToTestDappWithTwoChains() { - return this.withPermissionController({ - subjects: { - [DAPP_URL]: { - origin: DAPP_URL, - permissions: { - eth_accounts: { - id: 'ZaqPEWxyhNCJYACFw93jE', - parentCapability: 'eth_accounts', - invoker: DAPP_URL, - caveats: [ - { - type: 'restrictReturnedAccounts', - value: [DEFAULT_FIXTURE_ACCOUNT.toLowerCase()], - }, - ], - date: 1664388714636, - }, - 'endowment:permitted-chains': { - id: 'D7cac0a2e3BD8f349506a', - parentCapability: 'endowment:permitted-chains', - invoker: DAPP_URL, - caveats: [ - { - type: 'restrictNetworkSwitching', - value: ['0x539', '0x53a'], - }, - ], - date: 1664388714637, - }, - }, - }, - }, - }); - } - withPermissionControllerConnectedToTestDappWithTwoAccounts() { const subjects = { [DAPP_URL]: { From 2e5ea92c9c191d5c9abb9703625307262a76b990 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 22 Jan 2025 14:35:34 -0800 Subject: [PATCH 07/14] Update test/e2e/json-rpc/wallet_addEthereumChain.spec.ts Co-authored-by: Alex Donesky --- test/e2e/json-rpc/wallet_addEthereumChain.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index b188310fc73e..cfc5e589d14f 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -167,7 +167,7 @@ describe('Add Ethereum Chain', function () { assert.deepEqual(beforePermittedChains, []); - const switchEthereumChainRequest = JSON.stringify({ + const addEthereumChainRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_addEthereumChain', params: [ From 0d3c7f95c54b5e5afd726ef4e7d5a77dd766b6d0 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 22 Jan 2025 14:35:39 -0800 Subject: [PATCH 08/14] Update test/e2e/json-rpc/wallet_addEthereumChain.spec.ts Co-authored-by: Alex Donesky --- test/e2e/json-rpc/wallet_addEthereumChain.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index cfc5e589d14f..e3724499879b 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -187,7 +187,7 @@ describe('Add Ethereum Chain', function () { }); await driver.executeScript( - `window.ethereum.request(${switchEthereumChainRequest})`, + `window.ethereum.request(${addEthereumChainRequest})`, ); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); From d9b397bc50daa893c137db0f3d5af8635af065f7 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 22 Jan 2025 14:42:58 -0800 Subject: [PATCH 09/14] assert the approval is for a switch --- test/e2e/json-rpc/wallet_addEthereumChain.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index e3724499879b..7a63c1923382 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -191,6 +191,8 @@ describe('Add Ethereum Chain', function () { ); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.findElement({ text: 'Use your enabled networks' }); + await driver.findElement({ text: 'Localhost 8546' }); await driver.clickElement({ text: 'Confirm', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); From 6b5c97320415242e8fd9aaa9fdc51ed8cc9e8269 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 22 Jan 2025 14:49:13 -0800 Subject: [PATCH 10/14] jsdoc --- .../lib/rpc-method-middleware/handlers/ethereum-chain-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc71165917f1..3da7eb199cf8 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 @@ -165,11 +165,11 @@ export function validateAddEthereumChainParams(params) { * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. * @param {object} hooks - The hooks object. + * @param {Boolean} [hooks.autoApprove] - Whether or not any permission changes should prompt the user for approval or be automatically approved. * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. * @param {Function} hooks.requestPermittedChainsPermissionForOrigin - The callback to request a new permittedChains-equivalent CAIP-25 permission. * @param {Function} hooks.requestPermittedChainsPermissionIncrementalForOrigin - The callback to add a new chain to the permittedChains-equivalent CAIP-25 permission. - * @param hooks.autoApprove * @returns a null response on success or an error if user rejects an approval when autoApprove is false or on unexpected errors. */ export async function switchChain( From 06f8e6a9d6fc20af385da0b968898df5d847634a Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 22 Jan 2025 15:14:16 -0800 Subject: [PATCH 11/14] always call switchChain to ensure correct permittedChain permissions are set --- .../handlers/add-ethereum-chain.js | 25 ++-- .../handlers/add-ethereum-chain.test.js | 26 ----- .../json-rpc/wallet_addEthereumChain.spec.ts | 108 +++++++++++++++++- 3 files changed, 117 insertions(+), 42 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 5086424678ae..8d67875b3e41 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -182,20 +182,15 @@ async function addEthereumChainHandler( } } - // If the added or updated network is not the current chain, prompt the user to switch - if (chainId !== currentChainIdForDomain) { - const { networkClientId } = - updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - - return switchChain(res, end, chainId, networkClientId, { - autoApprove: shouldAddOrUpdateNetwork, - setActiveNetwork, - getCaveat, - requestPermittedChainsPermissionForOrigin, - requestPermittedChainsPermissionIncrementalForOrigin, - }); - } - res.result = null; - return end(); + const { networkClientId } = + updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; + + return switchChain(res, end, chainId, networkClientId, { + autoApprove: shouldAddOrUpdateNetwork, + setActiveNetwork, + getCaveat, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, + }); } 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 158f7487d5a4..39c1a5d1238d 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 @@ -338,30 +338,4 @@ describe('addEthereumChainHandler', () => { }), ); }); - - it('should add result set to null to response object if the requested rpcUrl (and chainId) is currently selected', async () => { - const CURRENT_RPC_CONFIG = createMockNonInfuraConfiguration(); - - const { mocks, response, handler } = createMockedHandler(); - mocks.getCurrentChainIdForDomain.mockReturnValue( - CURRENT_RPC_CONFIG.chainId, - ); - mocks.getNetworkConfigurationByChainId.mockReturnValue(CURRENT_RPC_CONFIG); - await handler({ - origin: 'example.com', - params: [ - { - chainId: CURRENT_RPC_CONFIG.chainId, - chainName: 'Custom Network', - rpcUrls: [CURRENT_RPC_CONFIG.rpcEndpoints[0].url], - nativeCurrency: { - symbol: CURRENT_RPC_CONFIG.nativeCurrency, - decimals: 18, - }, - blockExplorerUrls: ['https://custom.blockexplorer'], - }, - ], - }); - expect(response.result).toBeNull(); - }); }); diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index 7a63c1923382..520219fdb3ce 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -38,7 +38,7 @@ const getPermittedChains = async (driver: Driver) => { }; describe('Add Ethereum Chain', function () { - describe('the dapp is not already permitted to use the chain being added', () => { + describe('the dapp is not already permitted to use the chain being added and the dapp is on a different chain from the chain being added', () => { it('automatically permits and switches to the chain when the rpc endpoint is added and no rpc endpoint previously existed for the chain', async function () { await withFixtures( { @@ -204,6 +204,112 @@ describe('Add Ethereum Chain', function () { }); }); + describe('the dapp is not already permitted to use the chain being added and the dapp is on the same chain as the chain being added', () => { + it('automatically permits and switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver); + + assert.deepEqual(beforePermittedChains, []); + + const switchEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [ + { + chainId: '0x539', + chainName: 'Localhost 8545 alternative', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this does not match what already exists in the NetworkController + rpcUrls: ['http://127.0.0.1:8545'], + blockExplorerUrls: [], + }, + ], + }); + + await driver.executeScript( + `window.ethereum.request(${switchEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.clickElement({ text: 'Approve', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x539']); + }, + ); + }); + + it('prompts to switch to the chain when the rpc endpoint being added already exists', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver); + + assert.deepEqual(beforePermittedChains, []); + + const addEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [ + { + chainId: '0x539', + chainName: 'Localhost 8545', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this matches what already exists in the NetworkController + rpcUrls: ['http://localhost:8545'], + blockExplorerUrls: [], + }, + ], + }); + + await driver.executeScript( + `window.ethereum.request(${addEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.findElement({ text: 'Use your enabled networks' }); + await driver.findElement({ text: 'Localhost 8545' }); + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x539']); + }, + ); + }); + }); + describe('the dapp is already permitted to use the chain being added', () => { it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { await withFixtures( From e5f13fba1834b88020b25277d9f3000339f5da64 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 22 Jan 2025 15:37:39 -0800 Subject: [PATCH 12/14] update hooks.autoApprove jsdoc --- .../lib/rpc-method-middleware/handlers/ethereum-chain-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3da7eb199cf8..a507f4ff4dbd 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 @@ -165,7 +165,7 @@ export function validateAddEthereumChainParams(params) { * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. * @param {object} hooks - The hooks object. - * @param {Boolean} [hooks.autoApprove] - Whether or not any permission changes should prompt the user for approval or be automatically approved. + * @param {Boolean} [hooks.autoApprove] - A boolean indicating whether the request should prompt the user or be automatically approved. * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. * @param {Function} hooks.requestPermittedChainsPermissionForOrigin - The callback to request a new permittedChains-equivalent CAIP-25 permission. From 79214450fe7ac0594751155e762a3f80d87f91ac Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 23 Jan 2025 08:06:38 -0800 Subject: [PATCH 13/14] lint --- .../lib/rpc-method-middleware/handlers/add-ethereum-chain.js | 1 - .../lib/rpc-method-middleware/handlers/ethereum-chain-utils.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 8d67875b3e41..f7efcab693bd 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -182,7 +182,6 @@ async function addEthereumChainHandler( } } - const { networkClientId } = updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; 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 a507f4ff4dbd..0075729873d0 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 @@ -165,7 +165,7 @@ export function validateAddEthereumChainParams(params) { * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. * @param {object} hooks - The hooks object. - * @param {Boolean} [hooks.autoApprove] - A boolean indicating whether the request should prompt the user or be automatically approved. + * @param {boolean} [hooks.autoApprove] - A boolean indicating whether the request should prompt the user or be automatically approved. * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. * @param {Function} hooks.requestPermittedChainsPermissionForOrigin - The callback to request a new permittedChains-equivalent CAIP-25 permission. From a686e2baeac0e3a154017e30797e5c587ce21d45 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Thu, 23 Jan 2025 15:06:16 -0600 Subject: [PATCH 14/14] add test case (#29885) Add missing test case --- .../json-rpc/wallet_addEthereumChain.spec.ts | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts index 520219fdb3ce..d3a8108b2e1c 100644 --- a/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -310,7 +310,7 @@ describe('Add Ethereum Chain', function () { }); }); - describe('the dapp is already permitted to use the chain being added', () => { + describe('the dapp is already permitted to use the chain being added, and the dapp is on a different chain from the chain being added', () => { it('automatically switches to the chain when the rpc endpoint is added but a different rpc endpoint already existed for the chain', async function () { await withFixtures( { @@ -434,4 +434,65 @@ describe('Add Ethereum Chain', function () { ); }); }); + describe('the dapp is already permitted to use the chain being added, and the dapp is on the same chain as the chain being added, but the rpcEndpoint being proposed does not match any existing rpcEndpoints for the chain', () => { + it('prompts to add the rpc endpoint to the chain networkConfiguration and set it as the default', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDappWithChains(['0x539']) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + + const beforePermittedChains = await getPermittedChains(driver); + assert.deepEqual(beforePermittedChains, ['0x539']); + + await driver.findElement({ css: '#chainId', text: '0x539' }); + + const addEthereumChainRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_addEthereumChain', + params: [ + { + chainId: '0x539', + chainName: 'Alternative localhost chain 0x539', + nativeCurrency: { + name: '', + symbol: 'ETH', + decimals: 18, + }, + // this does not match what already exists in the NetworkController as an endpoint for this chain + rpcUrls: ['http://127.0.0.1:8545'], + blockExplorerUrls: [], + }, + ], + }); + + await driver.executeScript( + `window.ethereum.request(${addEthereumChainRequest})`, + ); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.findElement({ text: 'Update Localhost 8545' }); + await driver.clickElement({ text: 'Approve', tag: 'button' }); + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + + // go to network selector + await driver.findElement({ text: 'Localhost 8545' }); + await driver.clickElement({ text: 'Localhost 8545' }); + + await driver.findElement({ + text: 'Alternative localhost chain 0x539', + }); + }, + ); + }); + }); });