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 2fb3dc6c25c0..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 @@ -88,12 +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) - ) { + blockExplorerIndex !== existingNetwork.defaultBlockExplorerUrlIndex); + + if (shouldAddOrUpdateNetwork) { try { await requestUserApproval({ origin, @@ -180,20 +182,14 @@ 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, { - isAddFlow: true, - setActiveNetwork, - getCaveat, - requestPermittedChainsPermissionForOrigin, - requestPermittedChainsPermissionIncrementalForOrigin, - }); - } + const { networkClientId } = + updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - res.result = null; - return end(); + 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 dd01322b0249..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 @@ -184,7 +184,7 @@ describe('addEthereumChainHandler', () => { NON_INFURA_CHAIN_ID, 123, { - isAddFlow: true, + autoApprove: true, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: @@ -251,7 +251,7 @@ describe('addEthereumChainHandler', () => { '0x1', 123, { - isAddFlow: true, + autoApprove: true, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: @@ -264,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( @@ -298,7 +298,7 @@ describe('addEthereumChainHandler', () => { '0xa', createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, { - isAddFlow: true, + autoApprove: false, getCaveat: mocks.getCaveat, setActiveNetwork: mocks.setActiveNetwork, requestPermittedChainsPermissionForOrigin: @@ -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/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index db605e5fcc7a..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,12 +165,12 @@ 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.isAddFlow - The boolean determining if this call originates from wallet_addEthereumChain. + * @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. * @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. + * @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, @@ -178,7 +178,7 @@ export async function switchChain( chainId, networkClientId, { - isAddFlow, + autoApprove, setActiveNetwork, getCaveat, requestPermittedChainsPermissionForOrigin, @@ -197,13 +197,13 @@ export async function switchChain( if (!ethChainIds.includes(chainId)) { await requestPermittedChainsPermissionIncrementalForOrigin({ chainId, - autoApprove: isAddFlow, + autoApprove, }); } } else { await requestPermittedChainsPermissionForOrigin({ chainId, - autoApprove: isAddFlow, + autoApprove, }); } 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 c0a8f3c493a0..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 @@ -1,4 +1,4 @@ -import { rpcErrors } from '@metamask/rpc-errors'; +import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -10,7 +10,7 @@ describe('Ethereum Chain Utils', () => { const createMockedSwitchChain = () => { const end = jest.fn(); const mocks = { - isAddFlow: false, + autoApprove: false, setActiveNetwork: jest.fn(), getCaveat: jest.fn(), requestPermittedChainsPermissionForOrigin: jest.fn(), @@ -39,21 +39,10 @@ describe('Ethereum Chain Utils', () => { }); }); - it('passes through unexpected errors', async () => { - const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( - new Error('unexpected error'), - ); - - await switchChain('0x1', 'mainnet'); - - expect(end).toHaveBeenCalledWith(new Error('unexpected error')); - }); - 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; + mocks.autoApprove = false; await switchChain('0x1', 'mainnet'); expect( @@ -68,11 +57,11 @@ describe('Ethereum Chain Utils', () => { expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('should handle errors if the switch chain grant fails', async () => { + it('should throw an error if the switch chain approval is rejected', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( - new Error('failed to grant permittedChains'), - ); + mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce({ + code: errorCodes.provider.userRejectedRequest, + }); await switchChain('0x1', 'mainnet'); @@ -80,16 +69,16 @@ describe('Ethereum Chain Utils', () => { mocks.requestPermittedChainsPermissionForOrigin, ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); - expect(end).toHaveBeenCalledWith( - new Error('failed to grant permittedChains'), - ); + 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: {}, @@ -105,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: {}, @@ -123,10 +112,10 @@ describe('Ethereum Chain Utils', () => { expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('should handle errors if the permittedChains grant fails', async () => { + it('should throw errors if the permittedChains grant fails', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValueOnce( - new Error('failed to grant permittedChains'), + new Error('failed to auto grant permittedChains'), ); mocks.getCaveat.mockReturnValue({ value: { @@ -142,7 +131,7 @@ describe('Ethereum Chain Utils', () => { ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); expect(end).toHaveBeenCalledWith( - new Error('failed to grant permittedChains'), + new Error('failed to auto grant permittedChains'), ); }); }); 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..d3a8108b2e1c --- /dev/null +++ b/test/e2e/json-rpc/wallet_addEthereumChain.spec.ts @@ -0,0 +1,498 @@ +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, + unlockWallet, + WINDOW_TITLES, +} from '../helpers'; +import { PermissionNames } from '../../../app/scripts/controllers/permissions'; +import { CaveatTypes } from '../../../shared/constants/permissions'; + +const getPermittedChains = async (driver: 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 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( + { + 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 addEthereumChainRequest = 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(${addEthereumChainRequest})`, + ); + + 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); + + const afterPermittedChains = await getPermittedChains(driver); + assert.deepEqual(afterPermittedChains, ['0x53a']); + }, + ); + }); + }); + + 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, 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( + { + 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' }); + }, + ); + }); + }); + 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', + }); + }, + ); + }); + }); +});