mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Warn users when they attempt to add a network that is already configured (#11179)
* warn users when they attempt to add a network that is already configured * clean up validation logic * fixing up e2e tests * Update test/e2e/helpers.js Co-authored-by: Mark Stacey <markjstacey@gmail.com> Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
a8196c2b21
commit
e76762548c
@ -289,6 +289,9 @@
|
|||||||
"chainIdDefinition": {
|
"chainIdDefinition": {
|
||||||
"message": "The chain ID used to sign transactions for this network."
|
"message": "The chain ID used to sign transactions for this network."
|
||||||
},
|
},
|
||||||
|
"chainIdExistsErrorMsg": {
|
||||||
|
"message": "This Chain ID is currently used by the $1 network."
|
||||||
|
},
|
||||||
"chromeRequiredForHardwareWallets": {
|
"chromeRequiredForHardwareWallets": {
|
||||||
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
||||||
},
|
},
|
||||||
@ -2349,7 +2352,7 @@
|
|||||||
"message": "URLs require the appropriate HTTP/HTTPS prefix."
|
"message": "URLs require the appropriate HTTP/HTTPS prefix."
|
||||||
},
|
},
|
||||||
"urlExistsErrorMsg": {
|
"urlExistsErrorMsg": {
|
||||||
"message": "URL is already present in existing list of networks"
|
"message": "This URL is currently used by the $1 network."
|
||||||
},
|
},
|
||||||
"usePhishingDetection": {
|
"usePhishingDetection": {
|
||||||
"message": "Use Phishing Detection"
|
"message": "Use Phishing Detection"
|
||||||
|
@ -27,6 +27,7 @@ async function withFixtures(options, testSuite) {
|
|||||||
} = options;
|
} = options;
|
||||||
const fixtureServer = new FixtureServer();
|
const fixtureServer = new FixtureServer();
|
||||||
const ganacheServer = new Ganache();
|
const ganacheServer = new Ganache();
|
||||||
|
let secondaryGanacheServer;
|
||||||
let dappServer;
|
let dappServer;
|
||||||
let segmentServer;
|
let segmentServer;
|
||||||
let segmentStub;
|
let segmentStub;
|
||||||
@ -34,6 +35,16 @@ async function withFixtures(options, testSuite) {
|
|||||||
let webDriver;
|
let webDriver;
|
||||||
try {
|
try {
|
||||||
await ganacheServer.start(ganacheOptions);
|
await ganacheServer.start(ganacheOptions);
|
||||||
|
if (ganacheOptions?.concurrent) {
|
||||||
|
const { port, chainId } = ganacheOptions.concurrent;
|
||||||
|
secondaryGanacheServer = new Ganache();
|
||||||
|
await secondaryGanacheServer.start({
|
||||||
|
blockTime: 2,
|
||||||
|
_chainIdRpc: chainId,
|
||||||
|
port,
|
||||||
|
vmErrorsOnRPCResponse: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
await fixtureServer.start();
|
await fixtureServer.start();
|
||||||
await fixtureServer.loadState(path.join(__dirname, 'fixtures', fixtures));
|
await fixtureServer.loadState(path.join(__dirname, 'fixtures', fixtures));
|
||||||
if (dapp) {
|
if (dapp) {
|
||||||
@ -103,6 +114,9 @@ async function withFixtures(options, testSuite) {
|
|||||||
} finally {
|
} finally {
|
||||||
await fixtureServer.stop();
|
await fixtureServer.stop();
|
||||||
await ganacheServer.quit();
|
await ganacheServer.quit();
|
||||||
|
if (ganacheOptions?.concurrent) {
|
||||||
|
await secondaryGanacheServer.quit();
|
||||||
|
}
|
||||||
if (webDriver) {
|
if (webDriver) {
|
||||||
await webDriver.quit();
|
await webDriver.quit();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,49 @@ describe('Stores custom RPC history', function () {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
it(`creates first custom RPC entry`, async function () {
|
it(`creates first custom RPC entry`, async function () {
|
||||||
|
const port = 8546;
|
||||||
|
const chainId = 1338;
|
||||||
|
await withFixtures(
|
||||||
|
{
|
||||||
|
fixtures: 'imported-account',
|
||||||
|
ganacheOptions: { ...ganacheOptions, concurrent: { port, chainId } },
|
||||||
|
title: this.test.title,
|
||||||
|
},
|
||||||
|
async ({ driver }) => {
|
||||||
|
await driver.navigate();
|
||||||
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
|
const rpcUrl = `http://127.0.0.1:${port}`;
|
||||||
|
const networkName = 'Secondary Ganache Testnet';
|
||||||
|
|
||||||
|
await driver.clickElement('.network-display');
|
||||||
|
|
||||||
|
await driver.clickElement({ text: 'Custom RPC', tag: 'span' });
|
||||||
|
|
||||||
|
await driver.findElement('.settings-page__sub-header-text');
|
||||||
|
|
||||||
|
const customRpcInputs = await driver.findElements('input[type="text"]');
|
||||||
|
const networkNameInput = customRpcInputs[0];
|
||||||
|
const rpcUrlInput = customRpcInputs[1];
|
||||||
|
const chainIdInput = customRpcInputs[2];
|
||||||
|
|
||||||
|
await networkNameInput.clear();
|
||||||
|
await networkNameInput.sendKeys(networkName);
|
||||||
|
|
||||||
|
await rpcUrlInput.clear();
|
||||||
|
await rpcUrlInput.sendKeys(rpcUrl);
|
||||||
|
|
||||||
|
await chainIdInput.clear();
|
||||||
|
await chainIdInput.sendKeys(chainId.toString());
|
||||||
|
|
||||||
|
await driver.clickElement('.network-form__footer .btn-secondary');
|
||||||
|
await driver.findElement({ text: networkName, tag: 'div' });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('warns user when they enter url or chainId for an already configured network', async function () {
|
||||||
await withFixtures(
|
await withFixtures(
|
||||||
{
|
{
|
||||||
fixtures: 'imported-account',
|
fixtures: 'imported-account',
|
||||||
@ -23,8 +66,9 @@ describe('Stores custom RPC history', function () {
|
|||||||
await driver.fill('#password', 'correct horse battery staple');
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
await driver.press('#password', driver.Key.ENTER);
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
const rpcUrl = 'http://127.0.0.1:8545/1';
|
// duplicate network
|
||||||
const chainId = '0x539'; // Ganache default, decimal 1337
|
const duplicateRpcUrl = 'http://localhost:8545';
|
||||||
|
const duplicateChainId = '0x539';
|
||||||
|
|
||||||
await driver.clickElement('.network-display');
|
await driver.clickElement('.network-display');
|
||||||
|
|
||||||
@ -37,13 +81,19 @@ describe('Stores custom RPC history', function () {
|
|||||||
const chainIdInput = customRpcInputs[2];
|
const chainIdInput = customRpcInputs[2];
|
||||||
|
|
||||||
await rpcUrlInput.clear();
|
await rpcUrlInput.clear();
|
||||||
await rpcUrlInput.sendKeys(rpcUrl);
|
await rpcUrlInput.sendKeys(duplicateRpcUrl);
|
||||||
|
await driver.findElement({
|
||||||
|
text: 'This URL is currently used by the Localhost 8545 network.',
|
||||||
|
tag: 'p',
|
||||||
|
});
|
||||||
|
|
||||||
await chainIdInput.clear();
|
await chainIdInput.clear();
|
||||||
await chainIdInput.sendKeys(chainId);
|
await chainIdInput.sendKeys(duplicateChainId);
|
||||||
|
await driver.findElement({
|
||||||
await driver.clickElement('.network-form__footer .btn-secondary');
|
text:
|
||||||
await driver.findElement({ text: rpcUrl, tag: 'div' });
|
'This Chain ID is currently used by the Localhost 8545 network.',
|
||||||
|
tag: 'p',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
isSafeChainId,
|
isSafeChainId,
|
||||||
} from '../../../../../shared/modules/network.utils';
|
} from '../../../../../shared/modules/network.utils';
|
||||||
import { jsonRpcRequest } from '../../../../../shared/modules/rpc.utils';
|
import { jsonRpcRequest } from '../../../../../shared/modules/rpc.utils';
|
||||||
|
import { decimalToHex } from '../../../../helpers/utils/conversions.util';
|
||||||
|
|
||||||
const FORM_STATE_KEYS = [
|
const FORM_STATE_KEYS = [
|
||||||
'rpcUrl',
|
'rpcUrl',
|
||||||
@ -39,7 +40,7 @@ export default class NetworkForm extends PureComponent {
|
|||||||
isCurrentRpcTarget: PropTypes.bool,
|
isCurrentRpcTarget: PropTypes.bool,
|
||||||
blockExplorerUrl: PropTypes.string,
|
blockExplorerUrl: PropTypes.string,
|
||||||
rpcPrefs: PropTypes.object,
|
rpcPrefs: PropTypes.object,
|
||||||
rpcUrls: PropTypes.array,
|
networksToRender: PropTypes.array,
|
||||||
isFullScreen: PropTypes.bool,
|
isFullScreen: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -332,11 +333,25 @@ export default class NetworkForm extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
validateChainIdOnChange = (chainIdArg = '') => {
|
validateChainIdOnChange = (chainIdArg = '') => {
|
||||||
|
const { networksToRender } = this.props;
|
||||||
const chainId = chainIdArg.trim();
|
const chainId = chainIdArg.trim();
|
||||||
let errorMessage = '';
|
let errorMessage = '';
|
||||||
let radix = 10;
|
let radix = 10;
|
||||||
|
const hexChainId = chainId.startsWith('0x')
|
||||||
|
? chainId
|
||||||
|
: `0x${decimalToHex(chainId)}`;
|
||||||
|
const [matchingChainId] = networksToRender.filter(
|
||||||
|
(e) => e.chainId === hexChainId,
|
||||||
|
);
|
||||||
|
|
||||||
if (chainId.startsWith('0x')) {
|
if (chainId === '') {
|
||||||
|
this.setErrorTo('chainId', '');
|
||||||
|
return;
|
||||||
|
} else if (matchingChainId) {
|
||||||
|
errorMessage = this.context.t('chainIdExistsErrorMsg', [
|
||||||
|
matchingChainId.label ?? matchingChainId.labelKey,
|
||||||
|
]);
|
||||||
|
} else if (chainId.startsWith('0x')) {
|
||||||
radix = 16;
|
radix = 16;
|
||||||
if (!/^0x[0-9a-f]+$/iu.test(chainId)) {
|
if (!/^0x[0-9a-f]+$/iu.test(chainId)) {
|
||||||
errorMessage = this.context.t('invalidHexNumber');
|
errorMessage = this.context.t('invalidHexNumber');
|
||||||
@ -433,23 +448,29 @@ export default class NetworkForm extends PureComponent {
|
|||||||
|
|
||||||
validateUrlRpcUrl = (url, stateKey) => {
|
validateUrlRpcUrl = (url, stateKey) => {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
const { rpcUrls } = this.props;
|
const { networksToRender } = this.props;
|
||||||
const { chainId: stateChainId } = this.state;
|
const { chainId: stateChainId } = this.state;
|
||||||
const isValidUrl = validUrl.isWebUri(url) && url !== '';
|
const isValidUrl = validUrl.isWebUri(url);
|
||||||
const chainIdFetchFailed = this.hasError(
|
const chainIdFetchFailed = this.hasError(
|
||||||
'chainId',
|
'chainId',
|
||||||
t('failedToFetchChainId'),
|
t('failedToFetchChainId'),
|
||||||
);
|
);
|
||||||
|
const [matchingRPCUrl] = networksToRender.filter((e) => e.rpcUrl === url);
|
||||||
|
|
||||||
if (!isValidUrl) {
|
if (!isValidUrl && url !== '') {
|
||||||
this.setErrorTo(
|
this.setErrorTo(
|
||||||
stateKey,
|
stateKey,
|
||||||
this.context.t(
|
this.context.t(
|
||||||
this.isValidWhenAppended(url) ? 'urlErrorMsg' : 'invalidRPC',
|
this.isValidWhenAppended(url) ? 'urlErrorMsg' : 'invalidRPC',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (rpcUrls.includes(url)) {
|
} else if (matchingRPCUrl) {
|
||||||
this.setErrorTo(stateKey, this.context.t('urlExistsErrorMsg'));
|
this.setErrorTo(
|
||||||
|
stateKey,
|
||||||
|
this.context.t('urlExistsErrorMsg', [
|
||||||
|
matchingRPCUrl.label ?? matchingRPCUrl.labelKey,
|
||||||
|
]),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.setErrorTo(stateKey, '');
|
this.setErrorTo(stateKey, '');
|
||||||
}
|
}
|
||||||
|
@ -202,12 +202,12 @@ export default class NetworksTab extends PureComponent {
|
|||||||
{this.renderNetworksList()}
|
{this.renderNetworksList()}
|
||||||
{shouldRenderNetworkForm ? (
|
{shouldRenderNetworkForm ? (
|
||||||
<NetworkForm
|
<NetworkForm
|
||||||
rpcUrls={networksToRender.map((network) => network.rpcUrl)}
|
|
||||||
setRpcTarget={setRpcTarget}
|
setRpcTarget={setRpcTarget}
|
||||||
editRpc={editRpc}
|
editRpc={editRpc}
|
||||||
networkName={label || (labelKey && t(labelKey)) || ''}
|
networkName={label || (labelKey && t(labelKey)) || ''}
|
||||||
rpcUrl={rpcUrl}
|
rpcUrl={rpcUrl}
|
||||||
chainId={chainId}
|
chainId={chainId}
|
||||||
|
networksToRender={networksToRender}
|
||||||
ticker={ticker}
|
ticker={ticker}
|
||||||
onClear={(shouldUpdateHistory = true) => {
|
onClear={(shouldUpdateHistory = true) => {
|
||||||
setNetworksTabAddMode(false);
|
setNetworksTabAddMode(false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user