1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

eth_sign toggle Update in advanced settings (#18848)

* added eth sign first step

* added modal

* added validation for form

* updated width with block

* added state trigger for toggle

* updated Eth sign modal text changes

* added eth sign toggle tex

* removed unnecessary code

* fixed form validation text

* updated eth toggle text

* added test

* added analytics

* updated design changes

* lint fix

* updated error text

* updated changes
This commit is contained in:
Nidhi Kumari 2023-05-04 16:44:07 +05:30 committed by GitHub
parent bfbe10ba28
commit cfc653ada6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 453 additions and 11 deletions

View File

@ -4449,11 +4449,44 @@
"message": "To: $1",
"description": "$1 is the address to include in the To label. It is typically shortened first using shortenAddress"
},
"toggleEthSignBannerDescription": {
"message": "Youre at risk for phishing attacks. Protect yourself by turning off eth_sign."
},
"toggleEthSignDescriptionField": {
"message": "Turn this on to let dapps request your signature using eth_sign requests. eth_sign is an open-ended signing method that lets you sign an arbitrary hash, making it a dangerous phishing risk. Only sign eth_sign requests if you can read what you are signing and trust the origin of the request."
"message": "If you enable this setting, you might get signature requests that arent readable. By signing a message you don't understand, you could be agreeing to give away your funds and NFTs."
},
"toggleEthSignField": {
"message": "Toggle eth_sign requests"
"message": "Eth_sign requests"
},
"toggleEthSignModalBannerBoldText": {
"message": " you might be getting scammed"
},
"toggleEthSignModalBannerText": {
"message": "If you've been asked to turn this setting on,"
},
"toggleEthSignModalCheckBox": {
"message": "I understand that I can lose all of my funds and NFTs if I enable eth_sign requests. "
},
"toggleEthSignModalDescription": {
"message": "Allowing eth_sign requests can make you vulnerable to phishing attacks. Always review the URL and be careful when signing messages that contain code."
},
"toggleEthSignModalFormError": {
"message": "The text is incorrect"
},
"toggleEthSignModalFormLabel": {
"message": "Enter “I only sign what I understand” to continue"
},
"toggleEthSignModalFormValidation": {
"message": "I only sign what I understand"
},
"toggleEthSignModalTitle": {
"message": "Use at your own risk"
},
"toggleEthSignOff": {
"message": "OFF (Recommended)"
},
"toggleEthSignOn": {
"message": "ON (Not recommended)"
},
"toggleTestNetworks": {
"message": "$1 test networks",

View File

@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Eth Sign Modal should match snapshot 1`] = `
<div>
<div
class="box eth-sign-modal box--padding-4 box--display-flex box--flex-direction-column box--justify-content-flex-start"
>
<div
class="box box--margin-bottom-4 box--display-flex box--flex-direction-row box--justify-content-center"
>
<span
class="box eth-sign-modal__warning-icon mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-error-default"
style="mask-image: url('./images/icons/danger.svg');"
/>
<button
aria-label="Close"
class="box mm-button-icon mm-button-icon--size-sm eth-sign-modal__close box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-icon-default box--background-color-transparent box--rounded-lg"
>
<span
class="box mm-icon mm-icon--size-sm box--display-inline-block box--flex-direction-row box--color-inherit"
style="mask-image: url('./images/icons/close.svg');"
/>
</button>
</div>
<h3
class="box mm-text mm-text--heading-md mm-text--text-align-center box--margin-bottom-6 box--flex-direction-row box--color-text-default"
>
Use at your own risk
</h3>
<p
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
>
Allowing eth_sign requests can make you vulnerable to phishing attacks. Always review the URL and be careful when signing messages that contain code.
<a
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
href="https://support.metamask.io/hc/en-us/articles/14764161421467"
rel="noopener noreferrer"
target="_blank"
>
Learn more
</a>
</p>
<div
class="box mm-banner-base mm-banner-alert mm-banner-alert--severity-danger box--margin-top-6 box--margin-bottom-6 box--padding-3 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--background-color-error-muted box--rounded-sm"
>
<span
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-error-default"
style="mask-image: url('./images/icons/danger.svg');"
/>
<div>
If you've been asked to turn this setting on,
you might be getting scammed
</div>
</div>
<div
class="box box--gap-2 box--flex-direction-row box--align-items-flex-start box--display-flex"
>
<input
class="check-box eth-sign__checkbox far fa-square"
data-testid="eth-sign__checkbox"
id="eth-sign__checkbox"
readonly=""
type="checkbox"
/>
<label
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--display-inline-flex box--flex-direction-row box--align-items-center box--color-text-default"
for="eth-sign__checkbox"
>
<span
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
>
I understand that I can lose all of my funds and NFTs if I enable eth_sign requests.
</span>
</label>
</div>
<div
class="box box--margin-top-6 box--display-flex box--gap-4 box--flex-direction-row box--justify-content-space-between"
>
<button
class="box mm-text mm-button-base mm-button-base--size-lg mm-button-base--block mm-button-secondary mm-text--body-md box--padding-right-4 box--padding-left-4 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent box--rounded-pill box--border-color-primary-default box--border-style-solid box--border-width-1"
>
Cancel
</button>
<button
class="box mm-text mm-button-base mm-button-base--size-lg mm-button-base--disabled mm-button-base--block mm-button-primary mm-button-primary--disabled mm-text--body-md box--padding-right-4 box--padding-left-4 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-inverse box--background-color-primary-default box--rounded-pill"
disabled=""
>
Continue
</button>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,197 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
import Box from '../../../ui/box';
import {
BannerAlert,
ButtonIcon,
ButtonLink,
ButtonPrimary,
ButtonSecondary,
FormTextField,
Icon,
IconName,
IconSize,
Label,
Text,
} from '../../../component-library';
import {
AlignItems,
DISPLAY,
FLEX_DIRECTION,
IconColor,
JustifyContent,
SEVERITIES,
Size,
TextAlign,
TextVariant,
} from '../../../../helpers/constants/design-system';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import CheckBox from '../../../ui/check-box';
import { setDisabledRpcMethodPreference } from '../../../../store/actions';
import { getDisabledRpcMethodPreferences } from '../../../../selectors';
import {
MetaMetricsEventCategory,
MetaMetricsEventName,
} from '../../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../../contexts/metametrics';
const EthSignModal = ({ hideModal }) => {
const [isEthSignChecked, setIsEthSignChecked] = useState(false);
const [showTextField, setShowTextField] = useState(false);
const [inputKeyword, setInputKeyword] = useState('');
const disabledRpcMethodPreferences = useSelector(
getDisabledRpcMethodPreferences,
);
const t = useI18nContext();
const dispatch = useDispatch();
const trackEvent = useContext(MetaMetricsContext);
const handleSubmit = () => {
dispatch(
setDisabledRpcMethodPreference(
'eth_sign',
!disabledRpcMethodPreferences.eth_sign,
),
);
hideModal();
};
const isValid = inputKeyword === t('toggleEthSignModalFormValidation');
return (
<Box
className="eth-sign-modal"
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.COLUMN}
justifyContent={JustifyContent.flexStart}
padding={4}
>
<Box
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.ROW}
marginBottom={4}
justifyContent={JustifyContent.center}
>
<Icon
className="eth-sign-modal__warning-icon"
name={IconName.Danger}
color={IconColor.errorDefault}
size={IconSize.Lg}
/>
<ButtonIcon
className="eth-sign-modal__close"
iconName={IconName.Close}
size={Size.SM}
onClick={() => hideModal()}
ariaLabel={t('close')}
/>
</Box>
<Text
variant={TextVariant.headingMd}
textAlign={TextAlign.Center}
marginBottom={6}
>
{t('toggleEthSignModalTitle')}
</Text>
<Text variant={TextVariant.bodyMd}>
{t('toggleEthSignModalDescription')}
<ButtonLink
href="https://support.metamask.io/hc/en-us/articles/14764161421467"
externalLink
>
{t('learnMoreUpperCase')}
</ButtonLink>
</Text>
<BannerAlert severity={SEVERITIES.DANGER} marginTop={6} marginBottom={6}>
{t('toggleEthSignModalBannerText')}
{t('toggleEthSignModalBannerBoldText')}
</BannerAlert>
{showTextField ? (
<FormTextField
id="enter-eth-sign-text"
label={t('toggleEthSignModalFormLabel')}
error={inputKeyword.length > 0 && !isValid}
helpText={
inputKeyword.length > 0 &&
!isValid &&
t('toggleEthSignModalFormError')
}
onChange={(event) => setInputKeyword(event.target.value)}
value={inputKeyword}
onPaste={(event) => event.preventDefault()}
/>
) : (
<Box
flexDirection={FLEX_DIRECTION.ROW}
alignItems={AlignItems.flexStart}
gap={2}
>
<CheckBox
id="eth-sign__checkbox"
className="eth-sign__checkbox"
dataTestId="eth-sign__checkbox"
checked={isEthSignChecked}
onClick={() => {
setIsEthSignChecked(!isEthSignChecked);
}}
/>
<Label htmlFor="eth-sign__checkbox">
<Text variant={TextVariant.bodyMd} as="span">
{t('toggleEthSignModalCheckBox')}
</Text>
</Label>
</Box>
)}
<Box
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.ROW}
justifyContent={JustifyContent.spaceBetween}
gap={4}
marginTop={6}
>
<ButtonSecondary onClick={() => hideModal()} size={Size.LG} block>
{t('cancel')}
</ButtonSecondary>
{showTextField ? (
<ButtonPrimary
danger
block
disabled={!isValid}
onClick={handleSubmit}
size={Size.LG}
>
{t('enableSnap')}
</ButtonPrimary>
) : (
<ButtonPrimary
block
disabled={!isEthSignChecked}
size={Size.LG}
onClick={() => {
setShowTextField(true);
trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.OnboardingWalletAdvancedSettings,
properties: {
location: 'Settings',
enable_eth_sign: true,
},
});
}}
>
{t('continue')}
</ButtonPrimary>
)}
</Box>
</Box>
);
};
EthSignModal.propTypes = {
// The function to close the Modal
hideModal: PropTypes.func,
};
export default withModalProps(EthSignModal);

View File

@ -0,0 +1,9 @@
import React from 'react';
import EthSignModal from './eth-sign-modal';
export default {
title: 'Components/App/Modals/EthSignModal',
component: EthSignModal,
};
export const DefaultStory = (args) => <EthSignModal {...args} />;

View File

@ -0,0 +1,44 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import EthSignModal from './eth-sign-modal';
const mockHideModal = jest.fn();
const mockGetDisabledRpcMethodPreferences = jest.fn();
jest.mock('../../../../store/actions.ts', () => ({
...jest.requireActual('../../../../store/actions.ts'),
hideModal: () => mockHideModal,
getDisabledRpcMethodPreferences: () => mockGetDisabledRpcMethodPreferences,
}));
describe('Eth Sign Modal', () => {
const mockState = {
appState: {
modal: {
modalState: {
props: {},
},
},
},
metamask: {
provider: {
type: 'rpc',
chainId: '0x5',
},
disabledRpcMethodPreferences: { eth_sign: true },
},
};
const mockStore = configureMockStore([thunk])(mockState);
afterEach(() => {
jest.clearAllMocks();
});
it('should match snapshot', () => {
const { container } = renderWithProvider(<EthSignModal />, mockStore);
expect(container).toMatchSnapshot();
});
});

View File

@ -0,0 +1 @@
export { default } from './eth-sign-modal';

View File

@ -0,0 +1,8 @@
.eth-sign-modal {
position: relative;
&__close {
position: absolute;
right: 16px;
}
}

View File

@ -12,6 +12,7 @@
@import 'convert-token-to-nft-modal/index';
@import 'contract-details-modal/index';
@import 'hold-to-reveal-modal/index';
@import 'eth-sign-modal/index';
.modal {
z-index: 1050;

View File

@ -27,6 +27,8 @@ import NewAccountModal from './new-account-modal';
import CustomizeNonceModal from './customize-nonce';
import ConvertTokenToNftModal from './convert-token-to-nft-modal/convert-token-to-nft-modal';
import EthSignModal from './eth-sign-modal/eth-sign-modal';
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',
border: '1px solid var(--color-border-default)',
@ -160,6 +162,18 @@ const MODALS = {
},
},
ETH_SIGN: {
contents: <EthSignModal />,
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
CONFIRM_REMOVE_ACCOUNT: {
contents: <ConfirmRemoveAccount />,
mobileModalStyle: {

View File

@ -25,6 +25,11 @@ import {
import { exportAsFile } from '../../../helpers/utils/export-utils';
import ActionableMessage from '../../../components/ui/actionable-message';
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
import { BannerAlert } from '../../../components/component-library';
import {
SEVERITIES,
TextVariant,
} from '../../../helpers/constants/design-system';
const CORRUPT_JSON_FILE = 'CORRUPT_JSON_FILE';
@ -40,6 +45,7 @@ export default class AdvancedTab extends PureComponent {
setHexDataFeatureFlag: PropTypes.func,
displayWarning: PropTypes.func,
showResetAccountConfirmationModal: PropTypes.func,
showEthSignModal: PropTypes.func,
warning: PropTypes.string,
sendHexData: PropTypes.bool,
showFiatInTestnets: PropTypes.bool,
@ -539,10 +545,23 @@ export default class AdvancedTab extends PureComponent {
}
renderToggleEthSignControl() {
const { t } = this.context;
const { disabledRpcMethodPreferences, setDisabledRpcMethodPreference } =
this.props;
const { t, trackEvent } = this.context;
const {
disabledRpcMethodPreferences,
showEthSignModal,
setDisabledRpcMethodPreference,
} = this.props;
const toggleOff = (value) => {
setDisabledRpcMethodPreference('eth_sign', !value);
trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.OnboardingWalletAdvancedSettings,
properties: {
location: 'Settings',
enable_eth_sign: false,
},
});
};
return (
<div
ref={this.settingsRefs[10]}
@ -555,15 +574,26 @@ export default class AdvancedTab extends PureComponent {
{t('toggleEthSignDescriptionField')}
</div>
</div>
{disabledRpcMethodPreferences?.eth_sign === true ? (
<BannerAlert
severity={SEVERITIES.DANGER}
marginBottom={5}
descriptionProps={{ variant: TextVariant.bodyMd }}
>
{t('toggleEthSignBannerDescription')}
</BannerAlert>
) : null}
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<ToggleButton
className="eth-sign-toggle"
value={disabledRpcMethodPreferences?.eth_sign || false}
onToggle={(value) =>
setDisabledRpcMethodPreference('eth_sign', !value)
}
offLabel={t('off')}
onLabel={t('on')}
onToggle={(value) => {
value ? toggleOff(value) : showEthSignModal();
}}
offLabel={t('toggleEthSignOff')}
onLabel={t('toggleEthSignOn')}
/>
</div>
</div>

View File

@ -68,6 +68,7 @@ export const mapDispatchToProps = (dispatch) => {
displayWarning: (warning) => dispatch(displayWarning(warning)),
showResetAccountConfirmationModal: () =>
dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
showEthSignModal: () => dispatch(showModal({ name: 'ETH_SIGN' })),
setUseNonceField: (value) => dispatch(setUseNonceField(value)),
setShowFiatConversionOnTestnetsPreference: (value) => {
return dispatch(setShowFiatConversionOnTestnetsPreference(value));

View File

@ -27,6 +27,9 @@ export default {
showResetAccountConfirmationModal: {
action: 'showResetAccountConfirmationModal',
},
showEthSignModal: {
action: 'showEthSignModal',
},
},
};

View File

@ -379,6 +379,10 @@
max-width: 100%;
width: 100%;
}
.eth-sign-toggle .toggle-button__status { // for eth_sign we need to override the uppercase property of toggle button text
text-transform: capitalize;
}
}
&__content-item-col-open-sea {

View File

@ -585,6 +585,10 @@ export function getShowTestNetworks(state) {
return Boolean(showTestNetworks);
}
export function getDisabledRpcMethodPreferences(state) {
return state.metamask.disabledRpcMethodPreferences;
}
export function getShouldShowFiat(state) {
const isMainNet = getIsMainnet(state);
const isCustomNetwork = getIsCustomNetwork(state);