mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Allow adding networks with the same chainId as a preloaded/default network via wallet_AddEthereumChain
API (#16733)
* allow adding networks with the same chainId as an existing network via API Co-authored-by: Kurush Dubash <kurush@alchemyapi.io>
This commit is contained in:
parent
05ab94fd1c
commit
f162c58f5a
22
app/_locales/en/messages.json
generated
22
app/_locales/en/messages.json
generated
@ -207,6 +207,28 @@
|
|||||||
"addEthereumChainConfirmationTitle": {
|
"addEthereumChainConfirmationTitle": {
|
||||||
"message": "Allow this site to add a network?"
|
"message": "Allow this site to add a network?"
|
||||||
},
|
},
|
||||||
|
"addEthereumChainWarningModalHeader": {
|
||||||
|
"message": "Only add this RPC provider if you’re sure you can trust it. $1",
|
||||||
|
"description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded"
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalHeaderPartTwo": {
|
||||||
|
"message": "Malicious providers may lie about the state of the blockchain and record your network activity."
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalListHeader": {
|
||||||
|
"message": "It's important that your provider is reliable, as it has the power to:"
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalListPointOne": {
|
||||||
|
"message": "See your accounts and IP address, and associate them together"
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalListPointThree": {
|
||||||
|
"message": "Show account balances and other on-chain states"
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalListPointTwo": {
|
||||||
|
"message": "Broadcast your transactions"
|
||||||
|
},
|
||||||
|
"addEthereumChainWarningModalTitle": {
|
||||||
|
"message": "You are adding a new RPC provider for Ethereum Mainnet"
|
||||||
|
},
|
||||||
"addFriendsAndAddresses": {
|
"addFriendsAndAddresses": {
|
||||||
"message": "Add friends and addresses you trust"
|
"message": "Add friends and addresses you trust"
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,6 @@ 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 { CHAIN_ID_TO_NETWORK_ID_MAP } from '../../../../../shared/constants/network';
|
|
||||||
|
|
||||||
const addEthereumChain = {
|
const addEthereumChain = {
|
||||||
methodNames: [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN],
|
methodNames: [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN],
|
||||||
@ -140,14 +139,6 @@ async function addEthereumChainHandler(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CHAIN_ID_TO_NETWORK_ID_MAP[_chainId]) {
|
|
||||||
return end(
|
|
||||||
ethErrors.rpc.invalidParams({
|
|
||||||
message: `May not specify default MetaMask chain.`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingNetwork = findCustomRpcBy({ chainId: _chainId });
|
const existingNetwork = findCustomRpcBy({ chainId: _chainId });
|
||||||
|
|
||||||
// if the request is to add a network that is already added and configured
|
// if the request is to add a network that is already added and configured
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
@import 'beta-header/index';
|
@import 'beta-header/index';
|
||||||
@import 'cancel-speedup-popover/index';
|
@import 'cancel-speedup-popover/index';
|
||||||
@import 'confirm-page-container/index';
|
@import 'confirm-page-container/index';
|
||||||
|
@import 'confirmation-warning-modal/index';
|
||||||
@import 'confirm-page-container/enableEIP1559V2-notice';
|
@import 'confirm-page-container/enableEIP1559V2-notice';
|
||||||
@import 'collectibles-items/index';
|
@import 'collectibles-items/index';
|
||||||
@import 'collectibles-tab/index';
|
@import 'collectibles-tab/index';
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ConfirmationWarningModal from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/ConfirmationWarningModal',
|
||||||
|
id: __filename,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <ConfirmationWarningModal {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
@ -0,0 +1,105 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
|
|
||||||
|
import Popover from '../../ui/popover';
|
||||||
|
import Box from '../../ui/box';
|
||||||
|
import Button from '../../ui/button';
|
||||||
|
import Typography from '../../ui/typography';
|
||||||
|
import {
|
||||||
|
DISPLAY,
|
||||||
|
FLEX_DIRECTION,
|
||||||
|
FONT_WEIGHT,
|
||||||
|
JUSTIFY_CONTENT,
|
||||||
|
TYPOGRAPHY,
|
||||||
|
ALIGN_ITEMS,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
const ConfirmationWarningModal = ({ onSubmit, onCancel }) => {
|
||||||
|
const t = useI18nContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
className="confirmation-warning-modal__content"
|
||||||
|
footer={
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
|
||||||
|
className="confirmation-warning-modal__footer"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="confirmation-warning-modal__footer__approve-button"
|
||||||
|
type="danger-primary"
|
||||||
|
onClick={onSubmit}
|
||||||
|
>
|
||||||
|
{t('approveButtonText')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="confirmation-warning-modal__footer__cancel-button"
|
||||||
|
type="secondary"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
{t('reject')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
padding={3}
|
||||||
|
margin={0}
|
||||||
|
className="confirmation-warning-modal__content__header"
|
||||||
|
>
|
||||||
|
<i className="fa fa-exclamation-triangle confirmation-warning-modal__content__header__warning-icon" />
|
||||||
|
<Typography variant={TYPOGRAPHY.H4} fontWeight={FONT_WEIGHT.BOLD}>
|
||||||
|
{t('addEthereumChainWarningModalTitle')}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box marginLeft={6} marginRight={6} marginTop={0} marginBottom={3}>
|
||||||
|
<Typography marginTop={4} variant={TYPOGRAPHY.H6}>
|
||||||
|
{t('addEthereumChainWarningModalHeader', [
|
||||||
|
<strong key="part-2">
|
||||||
|
{t('addEthereumChainWarningModalHeaderPartTwo')}
|
||||||
|
</strong>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
<Typography marginTop={4} variant={TYPOGRAPHY.H6}>
|
||||||
|
{t('addEthereumChainWarningModalListHeader')}
|
||||||
|
</Typography>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<Typography marginTop={2} variant={TYPOGRAPHY.H6}>
|
||||||
|
{t('addEthereumChainWarningModalListPointOne')}
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Typography marginTop={2} variant={TYPOGRAPHY.H6}>
|
||||||
|
{t('addEthereumChainWarningModalListPointTwo')}
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Typography marginTop={2} variant={TYPOGRAPHY.H6}>
|
||||||
|
{t('addEthereumChainWarningModalListPointThree')}
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmationWarningModal.propTypes = {
|
||||||
|
/**
|
||||||
|
* Function that approves collection
|
||||||
|
*/
|
||||||
|
onSubmit: PropTypes.func,
|
||||||
|
/**
|
||||||
|
* Function that rejects collection
|
||||||
|
*/
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmationWarningModal;
|
1
ui/components/app/confirmation-warning-modal/index.js
Normal file
1
ui/components/app/confirmation-warning-modal/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './confirmation-warning-modal';
|
28
ui/components/app/confirmation-warning-modal/index.scss
Normal file
28
ui/components/app/confirmation-warning-modal/index.scss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.confirmation-warning-modal {
|
||||||
|
&__content {
|
||||||
|
ul {
|
||||||
|
padding-inline-start: 1rem;
|
||||||
|
margin-block-start: 0;
|
||||||
|
list-style: disc outside;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
border-bottom: 1px solid var(--color-border-muted);
|
||||||
|
|
||||||
|
&__warning-icon {
|
||||||
|
padding-top: 7px;
|
||||||
|
margin-right: 10px;
|
||||||
|
color: var(--color-error-default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import { produce } from 'immer';
|
|||||||
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
||||||
import Box from '../../components/ui/box';
|
import Box from '../../components/ui/box';
|
||||||
import MetaMaskTemplateRenderer from '../../components/app/metamask-template-renderer';
|
import MetaMaskTemplateRenderer from '../../components/app/metamask-template-renderer';
|
||||||
|
import ConfirmationWarningModal from '../../components/app/confirmation-warning-modal';
|
||||||
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
|
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
|
||||||
import {
|
import {
|
||||||
COLORS,
|
COLORS,
|
||||||
@ -32,7 +33,11 @@ import NetworkDisplay from '../../components/app/network-display/network-display
|
|||||||
import Callout from '../../components/ui/callout';
|
import Callout from '../../components/ui/callout';
|
||||||
import SiteOrigin from '../../components/ui/site-origin';
|
import SiteOrigin from '../../components/ui/site-origin';
|
||||||
import ConfirmationFooter from './components/confirmation-footer';
|
import ConfirmationFooter from './components/confirmation-footer';
|
||||||
import { getTemplateValues, getTemplateAlerts } from './templates';
|
import {
|
||||||
|
getTemplateValues,
|
||||||
|
getTemplateAlerts,
|
||||||
|
getTemplateState,
|
||||||
|
} from './templates';
|
||||||
|
|
||||||
// TODO(rekmarks): This component and all of its sub-components should probably
|
// TODO(rekmarks): This component and all of its sub-components should probably
|
||||||
// be renamed to "Dialog", now that we are using it in that manner.
|
// be renamed to "Dialog", now that we are using it in that manner.
|
||||||
@ -125,6 +130,28 @@ function useAlertState(pendingConfirmation) {
|
|||||||
return [alertState, dismissAlert];
|
return [alertState, dismissAlert];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useTemplateState(pendingConfirmation) {
|
||||||
|
const [templateState, setTemplateState] = useState({});
|
||||||
|
useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
if (pendingConfirmation) {
|
||||||
|
getTemplateState(pendingConfirmation).then((state) => {
|
||||||
|
if (isMounted && Object.values(state).length > 0) {
|
||||||
|
setTemplateState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[pendingConfirmation.id]: state,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
|
}, [pendingConfirmation]);
|
||||||
|
|
||||||
|
return [templateState];
|
||||||
|
}
|
||||||
|
|
||||||
export default function ConfirmationPage({
|
export default function ConfirmationPage({
|
||||||
redirectToHomeOnZeroConfirmations = true,
|
redirectToHomeOnZeroConfirmations = true,
|
||||||
}) {
|
}) {
|
||||||
@ -140,6 +167,8 @@ export default function ConfirmationPage({
|
|||||||
const pendingConfirmation = pendingConfirmations[currentPendingConfirmation];
|
const pendingConfirmation = pendingConfirmations[currentPendingConfirmation];
|
||||||
const originMetadata = useOriginMetadata(pendingConfirmation?.origin) || {};
|
const originMetadata = useOriginMetadata(pendingConfirmation?.origin) || {};
|
||||||
const [alertState, dismissAlert] = useAlertState(pendingConfirmation);
|
const [alertState, dismissAlert] = useAlertState(pendingConfirmation);
|
||||||
|
const [templateState] = useTemplateState(pendingConfirmation);
|
||||||
|
const [showWarningModal, setShowWarningModal] = useState(false);
|
||||||
|
|
||||||
const [inputStates, setInputStates] = useState({});
|
const [inputStates, setInputStates] = useState({});
|
||||||
const setInputState = (key, value) => {
|
const setInputState = (key, value) => {
|
||||||
@ -200,7 +229,9 @@ export default function ConfirmationPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = () =>
|
const handleSubmit = () =>
|
||||||
templatedValues.onSubmit(
|
templateState[pendingConfirmation.id]?.useWarningModal
|
||||||
|
? setShowWarningModal(true)
|
||||||
|
: templatedValues.onSubmit(
|
||||||
hasInputState(pendingConfirmation.type)
|
hasInputState(pendingConfirmation.type)
|
||||||
? inputStates[MESSAGE_TYPE.SNAP_DIALOG_PROMPT]
|
? inputStates[MESSAGE_TYPE.SNAP_DIALOG_PROMPT]
|
||||||
: null,
|
: null,
|
||||||
@ -291,6 +322,15 @@ export default function ConfirmationPage({
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<MetaMaskTemplateRenderer sections={templatedValues.content} />
|
<MetaMaskTemplateRenderer sections={templatedValues.content} />
|
||||||
|
{showWarningModal && (
|
||||||
|
<ConfirmationWarningModal
|
||||||
|
onSubmit={async () => {
|
||||||
|
await templatedValues.onSubmit();
|
||||||
|
setShowWarningModal(false);
|
||||||
|
}}
|
||||||
|
onCancel={templatedValues.onCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ConfirmationFooter
|
<ConfirmationFooter
|
||||||
alerts={
|
alerts={
|
||||||
|
@ -145,6 +145,13 @@ async function getAlerts(pendingApproval) {
|
|||||||
return alerts;
|
return alerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getState(pendingApproval) {
|
||||||
|
if (parseInt(pendingApproval.requestData.chainId, 16) === 1) {
|
||||||
|
return { useWarningModal: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
function getValues(pendingApproval, t, actions, history) {
|
function getValues(pendingApproval, t, actions, history) {
|
||||||
const originIsMetaMask = pendingApproval.origin === 'metamask';
|
const originIsMetaMask = pendingApproval.origin === 'metamask';
|
||||||
|
|
||||||
@ -346,6 +353,7 @@ function getValues(pendingApproval, t, actions, history) {
|
|||||||
const addEthereumChain = {
|
const addEthereumChain = {
|
||||||
getAlerts,
|
getAlerts,
|
||||||
getValues,
|
getValues,
|
||||||
|
getState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default addEthereumChain;
|
export default addEthereumChain;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user