diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index bd3750698..c5aacd5d5 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -263,6 +263,9 @@ "addToken": { "message": "Add token" }, + "addingCustomNetwork": { + "message": "Adding Network" + }, "address": { "message": "Address" }, @@ -1308,6 +1311,9 @@ "message": "Stack:", "description": "Title for error stack, which is displayed for debugging purposes" }, + "errorWhileConnectingToRPC": { + "message": "Error while connecting to the custom network." + }, "ethGasPriceFetchWarning": { "message": "Backup gas price is provided as the main gas estimation service is unavailable right now." }, @@ -2033,6 +2039,9 @@ "mismatchedNetworkSymbol": { "message": "The submitted currency symbol does not match what we expect for this chain ID." }, + "mismatchedRpcChainId": { + "message": "Chain ID returned by the custom network does not match the submitted chain ID." + }, "mismatchedRpcUrl": { "message": "According to our records the submitted RPC URL value does not match a known provider for this chain ID." }, 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 7d08d0719..150c7d351 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 @@ -10,7 +10,6 @@ import { isPrefixedFormattedHexString, isSafeChainId, } from '../../../../../shared/modules/network.utils'; -import { jsonRpcRequest } from '../../../../../shared/modules/rpc.utils'; const addEthereumChain = { methodNames: [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN], @@ -155,6 +154,7 @@ async function addEthereumChainHandler( if (currentChainId === _chainId && currentRpcUrl === firstValidRPCUrl) { return end(); } + // If this network is already added with but is not the currently selected network // Ask the user to switch the network try { @@ -182,28 +182,6 @@ async function addEthereumChainHandler( return end(); } - let endpointChainId; - - try { - endpointChainId = await jsonRpcRequest(firstValidRPCUrl, 'eth_chainId'); - } catch (err) { - return end( - ethErrors.rpc.internal({ - message: `Request for method 'eth_chainId on ${firstValidRPCUrl} failed`, - data: { networkErr: err }, - }), - ); - } - - if (_chainId !== endpointChainId) { - return end( - ethErrors.rpc.invalidParams({ - message: `Chain ID returned by RPC URL ${firstValidRPCUrl} does not match ${_chainId}`, - data: { chainId: endpointChainId }, - }), - ); - } - if (typeof chainName !== 'string' || !chainName) { return end( ethErrors.rpc.invalidParams({ @@ -266,20 +244,18 @@ async function addEthereumChainHandler( } try { - await addCustomRpc( - await requestUserApproval({ - origin, - type: MESSAGE_TYPE.ADD_ETHEREUM_CHAIN, - requestData: { - chainId: _chainId, - blockExplorerUrl: firstValidBlockExplorerUrl, - chainName: _chainName, - rpcUrl: firstValidRPCUrl, - ticker, - }, - }), - ); - + const customRpc = await requestUserApproval({ + origin, + type: MESSAGE_TYPE.ADD_ETHEREUM_CHAIN, + requestData: { + chainId: _chainId, + blockExplorerUrl: firstValidBlockExplorerUrl, + chainName: _chainName, + rpcUrl: firstValidRPCUrl, + ticker, + }, + }); + await addCustomRpc(customRpc); sendMetrics({ event: 'Custom Network Added', category: EVENT.CATEGORIES.NETWORK, diff --git a/coverage-targets.js b/coverage-targets.js index 7c48ace8e..83ee2c5cc 100644 --- a/coverage-targets.js +++ b/coverage-targets.js @@ -6,10 +6,10 @@ // subset of files to check against these targets. module.exports = { global: { - lines: 64, - branches: 52.5, - statements: 63.1, - functions: 56.1, + lines: 64.5, + branches: 53, + statements: 63, + functions: 56.5, }, transforms: { branches: 100, diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js index 9213a6e5b..cda544ded 100644 --- a/test/e2e/mock-e2e.js +++ b/test/e2e/mock-e2e.js @@ -32,6 +32,20 @@ async function setupMocking(server, testSpecificMock) { return {}; }, }); + await server + .forPost( + 'https://arbitrum-mainnet.infura.io/v3/00000000000000000000000000000000', + ) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1675864782845', + result: '0xa4b1', + }, + }; + }); await server.forPost('https://api.segment.io/v1/batch').thenCallback(() => { return { @@ -372,6 +386,19 @@ async function setupMocking(server, testSpecificMock) { json: emptyHotlist, }; }); + + await server + .forPost('https://customnetwork.com/api/customRPC') + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1675864782845', + result: '0x122', + }, + }; + }); } module.exports = { setupMocking }; diff --git a/test/e2e/tests/add-custom-network.spec.js b/test/e2e/tests/add-custom-network.spec.js index b43a8a3ae..c46ca2694 100644 --- a/test/e2e/tests/add-custom-network.spec.js +++ b/test/e2e/tests/add-custom-network.spec.js @@ -17,6 +17,213 @@ describe('Custom network', function () { }, ], }; + + it('should show warning when adding chainId 0x1(ethereum) and be followed by an wrong chainId error', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + await driver.openNewPage('http://127.0.0.1:8080/'); + await driver.executeScript(` + var params = [{ + chainId: "0x1", + chainName: "Fake Ethereum Network", + nativeCurrency: { + name: "", + symbol: "ETH", + decimals: 18 + }, + rpcUrls: ["https://customnetwork.com/api/customRPC"], + blockExplorerUrls: [ "http://localhost:8080/api/customRPC" ] + }] + window.ethereum.request({ + method: 'wallet_addEthereumChain', + params + }) + `); + const windowHandles = await driver.waitUntilXWindowHandles(3); + + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + + await driver.clickElement({ + tag: 'button', + text: 'Approve', + }); + + const warningTxt = + 'You are adding a new RPC provider for Ethereum Mainnet'; + + await driver.findElement({ + tag: 'h4', + text: warningTxt, + }); + + await driver.clickElement({ + tag: 'button', + text: 'Approve', + }); + + const errMsg = + 'Chain ID returned by the custom network does not match the submitted chain ID.'; + await driver.findElement({ + tag: 'span', + text: errMsg, + }); + + const approveBtn = await driver.findElement({ + tag: 'button', + text: 'Approve', + }); + + assert.equal(await approveBtn.isEnabled(), false); + await driver.clickElement({ + tag: 'button', + text: 'Cancel', + }); + }, + ); + }); + it("don't add bad rpc custom network", async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + await driver.openNewPage('http://127.0.0.1:8080/'); + await driver.executeScript(` + var params = [{ + chainId: "0x123", + chainName: "Antani", + nativeCurrency: { + name: "", + symbol: "ANTANI", + decimals: 18 + }, + rpcUrls: ["https://customnetwork.com/api/customRPC"], + blockExplorerUrls: [ "http://localhost:8080/api/customRPC" ] + }] + window.ethereum.request({ + method: 'wallet_addEthereumChain', + params + }) + `); + const windowHandles = await driver.waitUntilXWindowHandles(3); + + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + await driver.clickElement({ + tag: 'button', + text: 'Approve', + }); + + const errMsg = + 'Chain ID returned by the custom network does not match the submitted chain ID.'; + await driver.findElement({ + tag: 'span', + text: errMsg, + }); + + const approveBtn = await driver.findElement({ + tag: 'button', + text: 'Approve', + }); + + assert.equal(await approveBtn.isEnabled(), false); + await driver.clickElement({ + tag: 'button', + text: 'Cancel', + }); + }, + ); + }); + + it("don't add unreachable custom network", async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + await driver.openNewPage('http://127.0.0.1:8080/'); + await driver.executeScript(` + var params = [{ + chainId: "0x123", + chainName: "Antani", + nativeCurrency: { + name: "", + symbol: "ANTANI", + decimals: 18 + }, + rpcUrls: ["https://doesntexist.abc/customRPC"], + blockExplorerUrls: [ "http://localhost:8080/api/customRPC" ] + }] + window.ethereum.request({ + method: 'wallet_addEthereumChain', + params + }) + `); + const windowHandles = await driver.waitUntilXWindowHandles(3); + + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + await driver.clickElement({ + tag: 'button', + text: 'Approve', + }); + + await driver.findElement({ + tag: 'span', + text: 'Error while connecting to the custom network.', + }); + + const approveBtn = await driver.findElement({ + tag: 'button', + text: 'Approve', + }); + + assert.equal(await approveBtn.isEnabled(), false); + await driver.clickElement({ + tag: 'button', + text: 'Cancel', + }); + }, + ); + }); + it('add custom network and switch the network', async function () { await withFixtures( { @@ -85,7 +292,6 @@ describe('Custom network', function () { await driver.clickElement({ tag: 'button', text: 'Close' }); await driver.clickElement({ tag: 'button', text: 'Approve' }); - await driver.clickElement({ tag: 'h6', text: 'Switch to Arbitrum One', diff --git a/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js b/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js index d73149fb8..30f116d3f 100644 --- a/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js +++ b/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js @@ -8,11 +8,15 @@ export default function ConfirmationFooter({ onCancel, submitText, cancelText, + loadingText, alerts, + loading, + submitAlerts, }) { return (