diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 078fda863..04d4bbfe2 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -4140,6 +4140,12 @@ "transactionResubmitted": { "message": "Transaction resubmitted with estimated gas fee increased to $1 at $2" }, + "transactionSecurityCheck": { + "message": "Transaction security check" + }, + "transactionSecurityCheckDescription": { + "message": "Turn this on to enable a third party (OpenSea) to review all your transactions and signature requests and warn you about known malicious requests." + }, "transactionSubmitted": { "message": "Transaction submitted with estimated gas fee of $1 at $2." }, diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 569d09750..b99d304ee 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -70,6 +70,7 @@ export default class PreferencesController { : LEDGER_TRANSPORT_TYPES.U2F, theme: 'light', improvedTokenAllowanceEnabled: false, + transactionSecurityCheckEnabled: false, ...opts.initState, }; @@ -199,6 +200,17 @@ export default class PreferencesController { }); } + /** + * Setter for the `transactionSecurityCheckEnabled` property + * + * @param transactionSecurityCheckEnabled + */ + setTransactionSecurityCheckEnabled(transactionSecurityCheckEnabled) { + this.store.updateState({ + transactionSecurityCheckEnabled, + }); + } + /** * Add new methodData to state, to avoid requesting this information again through Infura * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b56b17f2e..02d2dd530 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1648,6 +1648,10 @@ export default class MetamaskController extends EventEmitter { preferencesController.setImprovedTokenAllowanceEnabled.bind( preferencesController, ), + setTransactionSecurityCheckEnabled: + preferencesController.setTransactionSecurityCheckEnabled.bind( + preferencesController, + ), // AssetsContractController getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this), diff --git a/ui/helpers/constants/settings.js b/ui/helpers/constants/settings.js index 9072b48d3..f53fed4a0 100644 --- a/ui/helpers/constants/settings.js +++ b/ui/helpers/constants/settings.js @@ -343,4 +343,11 @@ export const SETTINGS_CONSTANTS = [ route: `${EXPERIMENTAL_ROUTE}#improved-token-allowance`, icon: 'fa fa-flask', }, + { + tabMessage: (t) => t('experimental'), + sectionMessage: (t) => t('transactionSecurityCheck'), + descriptionMessage: (t) => t('transactionSecurityCheckDescription'), + route: `${EXPERIMENTAL_ROUTE}#transaction-security-check`, + icon: 'fa fa-flask', + }, ]; diff --git a/ui/helpers/utils/settings-search.test.js b/ui/helpers/utils/settings-search.test.js index 96210eae1..9efe18bc5 100644 --- a/ui/helpers/utils/settings-search.test.js +++ b/ui/helpers/utils/settings-search.test.js @@ -182,7 +182,7 @@ describe('Settings Search Utils', () => { it('should get good experimental section number', () => { expect(getNumberOfSettingsInSection(t, t('experimental'))).toStrictEqual( - 2, + 3, ); }); diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.js b/ui/pages/settings/experimental-tab/experimental-tab.component.js index fc05a8620..d4c362b4b 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.js @@ -22,6 +22,8 @@ export default class ExperimentalTab extends PureComponent { setEIP1559V2Enabled: PropTypes.func, improvedTokenAllowanceEnabled: PropTypes.bool, setImprovedTokenAllowanceEnabled: PropTypes.func, + transactionSecurityCheckEnabled: PropTypes.bool, + setTransactionSecurityCheckEnabled: PropTypes.func, }; settingsRefs = Array( @@ -198,7 +200,7 @@ export default class ExperimentalTab extends PureComponent { this.props; return ( -
+
{t('improvedTokenAllowance')}
@@ -229,9 +231,50 @@ export default class ExperimentalTab extends PureComponent { ); } + renderTransactionSecurityCheckToggle() { + const { t } = this.context; + + const { + transactionSecurityCheckEnabled, + setTransactionSecurityCheckEnabled, + } = this.props; + + return ( +
+
+ {t('transactionSecurityCheck')} +
+ {t('transactionSecurityCheckDescription')} +
+
+
+
+ { + this.context.trackEvent({ + category: EVENT.CATEGORIES.SETTINGS, + event: 'Enabled/Disable TransactionSecurityCheck', + properties: { + action: 'Enabled/Disable TransactionSecurityCheck', + legacy_event: true, + }, + }); + setTransactionSecurityCheckEnabled(!value); + }} + offLabel={t('off')} + onLabel={t('on')} + /> +
+
+
+ ); + } + render() { return (
+ {this.renderTransactionSecurityCheckToggle()} {this.renderImprovedTokenAllowanceToggle()} {this.renderOpenSeaEnabledToggle()} {this.renderCollectibleDetectionToggle()} diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.js b/ui/pages/settings/experimental-tab/experimental-tab.container.js index 16d2fbfb3..d4e082ef5 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.js @@ -6,12 +6,14 @@ import { setOpenSeaEnabled, setEIP1559V2Enabled, setImprovedTokenAllowanceEnabled, + setTransactionSecurityCheckEnabled, } from '../../../store/actions'; import { getUseNftDetection, getOpenSeaEnabled, getEIP1559V2Enabled, getIsImprovedTokenAllowanceEnabled, + getIsTransactionSecurityCheckEnabled, } from '../../../selectors'; import ExperimentalTab from './experimental-tab.component'; @@ -21,6 +23,8 @@ const mapStateToProps = (state) => { openSeaEnabled: getOpenSeaEnabled(state), eip1559V2Enabled: getEIP1559V2Enabled(state), improvedTokenAllowanceEnabled: getIsImprovedTokenAllowanceEnabled(state), + transactionSecurityCheckEnabled: + getIsTransactionSecurityCheckEnabled(state), }; }; @@ -31,6 +35,8 @@ const mapDispatchToProps = (dispatch) => { setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)), setImprovedTokenAllowanceEnabled: (val) => dispatch(setImprovedTokenAllowanceEnabled(val)), + setTransactionSecurityCheckEnabled: (val) => + dispatch(setTransactionSecurityCheckEnabled(val)), }; }; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 9567f3443..7d937b789 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1270,6 +1270,16 @@ export function getIsImprovedTokenAllowanceEnabled(state) { return state.metamask.improvedTokenAllowanceEnabled; } +/** + * To get the `transactionSecurityCheckEnabled` value which determines whether we use the transaction security check + * + * @param {*} state + * @returns Boolean + */ +export function getIsTransactionSecurityCheckEnabled(state) { + return state.metamask.transactionSecurityCheckEnabled; +} + export function getIsCustomNetwork(state) { const chainId = getCurrentChainId(state); diff --git a/ui/store/actions.js b/ui/store/actions.js index 1d79a543d..3fc17fc61 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -3803,6 +3803,20 @@ export function setImprovedTokenAllowanceEnabled( }; } +export function setTransactionSecurityCheckEnabled( + transactionSecurityCheckEnabled, +) { + return async () => { + try { + await submitRequestToBackground('setTransactionSecurityCheckEnabled', [ + transactionSecurityCheckEnabled, + ]); + } catch (error) { + log.error(error); + } + }; +} + export function setFirstTimeUsedNetwork(chainId) { return submitRequestToBackground('setFirstTimeUsedNetwork', [chainId]); }