From 2d7c9b3dacc08c3d308100894b14d8c9f971e775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Mi=C3=B1o?= Date: Wed, 23 Jan 2019 15:09:56 -0300 Subject: [PATCH] Prevent send to token warning (#6058) --- development/states/add-token.json | 1 + development/states/confirm-new-ui.json | 3 +- development/states/confirm-sig-requests.json | 1 + development/states/currency-localization.json | 1 + development/states/navigate-txs.json | 3 +- development/states/send-edit.json | 4 ++- development/states/send-new-ui.json | 2 ++ development/states/tx-list-items.json | 3 +- ui/app/actions.js | 9 ++++++ ui/app/components/ens-input.js | 2 +- .../send-row-warning-message/index.js | 1 + .../send-row-warning-message.component.js | 27 ++++++++++++++++++ .../send-row-warning-message.container.js | 12 ++++++++ .../send-row-warning-message.scss | 0 ...send-row-warning-message-component.test.js | 28 +++++++++++++++++++ ...send-row-warning-message-container.test.js | 28 +++++++++++++++++++ .../send-row-wrapper.component.js | 7 ++++- .../send-to-row/send-to-row.component.js | 19 +++++++++---- .../send-to-row/send-to-row.container.js | 6 ++++ .../send-to-row/send-to-row.selectors.js | 5 ++++ .../send-to-row/send-to-row.utils.js | 9 +++++- .../tests/send-to-row-component.test.js | 21 ++++++++++++-- .../tests/send-to-row-container.test.js | 15 ++++++++++ .../tests/send-to-row-utils.test.js | 26 +++++++++++++++++ ui/app/components/send/send.selectors.js | 5 ++++ ui/app/css/itcss/components/send.scss | 7 +++++ ui/app/ducks/send.duck.js | 16 +++++++++++ ui/app/ducks/tests/send-duck.test.js | 10 +++++++ 28 files changed, 256 insertions(+), 15 deletions(-) create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/index.js create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.component.js create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.container.js create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.scss create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-component.test.js create mode 100644 ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-container.test.js diff --git a/development/states/add-token.json b/development/states/add-token.json index b59e9b757..6de25664a 100644 --- a/development/states/add-token.json +++ b/development/states/add-token.json @@ -104,6 +104,7 @@ "amount": "0x0", "memo": "", "errors": {}, + "warnings": {}, "maxModeOn": false, "editingTransactionId": null }, diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json index a5138c05a..346f06c6b 100644 --- a/development/states/confirm-new-ui.json +++ b/development/states/confirm-new-ui.json @@ -159,7 +159,8 @@ "send": { "fromDropdownOpen": false, "toDropdownOpen": false, - "errors": {} + "errors": {}, + "warnings": {} }, "confirmTransaction": { "txData": {}, diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json index f41327f16..613112ec9 100644 --- a/development/states/confirm-sig-requests.json +++ b/development/states/confirm-sig-requests.json @@ -148,6 +148,7 @@ "amount": "0x1bc16d674ec80000", "memo": "", "errors": {}, + "warnings": {}, "maxModeOn": false, "editingTransactionId": null }, diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index b36d7d2d2..a4ca19eb6 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -106,6 +106,7 @@ "amount": "0x0", "memo": "", "errors": {}, + "warnings": {}, "maxModeOn": false, "editingTransactionId": null }, diff --git a/development/states/navigate-txs.json b/development/states/navigate-txs.json index d8b8dc67f..813b02172 100644 --- a/development/states/navigate-txs.json +++ b/development/states/navigate-txs.json @@ -280,7 +280,8 @@ "send": { "fromDropdownOpen": false, "toDropdownOpen": false, - "errors": {} + "errors": {}, + "warnings": {} }, "confirmTransaction": { "txData": { diff --git a/development/states/send-edit.json b/development/states/send-edit.json index 5470f4396..0157dece4 100644 --- a/development/states/send-edit.json +++ b/development/states/send-edit.json @@ -128,6 +128,7 @@ "amount": "0x1bc16d674ec80000", "memo": "", "errors": {}, + "warnings": {}, "maxModeOn": false, "editingTransactionId": null }, @@ -161,7 +162,8 @@ "send": { "fromDropdownOpen": false, "toDropdownOpen": false, - "errors": {} + "errors": {}, + "warnings": {} }, "confirmTransaction": { "txData": {}, diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json index 0a457da0e..88bffbd48 100644 --- a/development/states/send-new-ui.json +++ b/development/states/send-new-ui.json @@ -107,6 +107,7 @@ "amount": "0x0", "memo": "", "errors": {}, + "warnings": {}, "maxModeOn": false, "editingTransactionId": null }, @@ -141,6 +142,7 @@ "fromDropdownOpen": false, "toDropdownOpen": false, "errors": {}, + "warnings": {}, "gasButtonGroupShown": true }, "confirmTransaction": { diff --git a/development/states/tx-list-items.json b/development/states/tx-list-items.json index d41fa664a..990f97ded 100644 --- a/development/states/tx-list-items.json +++ b/development/states/tx-list-items.json @@ -133,7 +133,8 @@ "send": { "fromDropdownOpen": false, "toDropdownOpen": false, - "errors": {} + "errors": {}, + "warnings": {} }, "gas": { "customData": { diff --git a/ui/app/actions.js b/ui/app/actions.js index 29cf4b2f2..c2622d0a0 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -191,6 +191,7 @@ var actions = { UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO', UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS', + UPDATE_SEND_WARNINGS: 'UPDATE_SEND_WARNINGS', UPDATE_MAX_MODE: 'UPDATE_MAX_MODE', UPDATE_SEND: 'UPDATE_SEND', CLEAR_SEND: 'CLEAR_SEND', @@ -211,6 +212,7 @@ var actions = { setMaxModeTo, updateSend, updateSendErrors, + updateSendWarnings, clearSend, setSelectedAddress, gasLoadingStarted, @@ -1068,6 +1070,13 @@ function updateSendErrors (errorObject) { } } +function updateSendWarnings (warningObject) { + return { + type: actions.UPDATE_SEND_WARNINGS, + value: warningObject, + } +} + function setSendTokenBalance (tokenBalance) { return { type: actions.UPDATE_SEND_TOKEN_BALANCE, diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index f538fd555..a9167e3b2 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -128,7 +128,7 @@ EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) { } if (prevState && ensResolution && this.props.onChange && ensResolution !== prevState.ensResolution) { - this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError }) + this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError, toWarning: state.toWarning }) } } diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/index.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/index.js new file mode 100644 index 000000000..fd4d19ef7 --- /dev/null +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/index.js @@ -0,0 +1 @@ +export { default } from './send-row-warning-message.container' diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.component.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.component.js new file mode 100644 index 000000000..f1caa8f99 --- /dev/null +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.component.js @@ -0,0 +1,27 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +export default class SendRowWarningMessage extends Component { + + static propTypes = { + warnings: PropTypes.object, + warningType: PropTypes.string, + }; + + static contextTypes = { + t: PropTypes.func, + }; + + render () { + const { warnings, warningType } = this.props + + const warningMessage = warningType in warnings && warnings[warningType] + + return ( + warningMessage + ?
{this.context.t(warningMessage)}
+ : null + ) + } + +} diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.container.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.container.js new file mode 100644 index 000000000..7df14fd96 --- /dev/null +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.container.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux' +import { getSendWarnings } from '../../../send.selectors' +import SendRowWarningMessage from './send-row-warning-message.component' + +export default connect(mapStateToProps)(SendRowWarningMessage) + +function mapStateToProps (state, ownProps) { + return { + warnings: getSendWarnings(state), + warningType: ownProps.warningType, + } +} diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.scss b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/send-row-warning-message.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-component.test.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-component.test.js new file mode 100644 index 000000000..bd803d833 --- /dev/null +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-component.test.js @@ -0,0 +1,28 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import SendRowWarningMessage from '../send-row-warning-message.component.js' + +describe('SendRowWarningMessage Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow(, { context: { t: str => str + '_t' } }) + }) + + describe('render', () => { + it('should render null if the passed warnings do not contain a warning of warningType', () => { + assert.equal(wrapper.find('.send-v2__warning').length, 0) + assert.equal(wrapper.html(), null) + }) + + it('should render a warning message if the passed warnings contain a warning of warningType', () => { + wrapper.setProps({ warnings: { warning1: 'abc', warning2: 'def', warning3: 'xyz' } }) + assert.equal(wrapper.find('.send-v2__warning').length, 1) + assert.equal(wrapper.find('.send-v2__warning').text(), 'xyz_t') + }) + }) +}) diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-container.test.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-container.test.js new file mode 100644 index 000000000..225bf056c --- /dev/null +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-warning-message/tests/send-row-warning-message-container.test.js @@ -0,0 +1,28 @@ +import assert from 'assert' +import proxyquire from 'proxyquire' + +let mapStateToProps + +proxyquire('../send-row-warning-message.container.js', { + 'react-redux': { + connect: (ms, md) => { + mapStateToProps = ms + return () => ({}) + }, + }, + '../../../send.selectors': { getSendWarnings: (s) => `mockWarnings:${s}` }, +}) + +describe('send-row-warning-message container', () => { + + describe('mapStateToProps()', () => { + + it('should map the correct properties to props', () => { + assert.deepEqual(mapStateToProps('mockState', { warningType: 'someType' }), { + warnings: 'mockWarnings:mockState', + warningType: 'someType' }) + }) + + }) + +}) diff --git a/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js index 04f4f8a15..0146ce645 100644 --- a/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js @@ -1,6 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import SendRowErrorMessage from './send-row-error-message/' +import SendRowWarningMessage from './send-row-warning-message/' export default class SendRowWrapper extends Component { @@ -9,6 +10,8 @@ export default class SendRowWrapper extends Component { errorType: PropTypes.string, label: PropTypes.string, showError: PropTypes.bool, + showWarning: PropTypes.bool, + warningType: PropTypes.string, }; static contextTypes = { @@ -21,8 +24,9 @@ export default class SendRowWrapper extends Component { errorType = '', label, showError = false, + showWarning = false, + warningType = '', } = this.props - const formField = Array.isArray(children) ? children[1] || children[0] : children const customLabelContent = children.length > 1 ? children[0] : null @@ -31,6 +35,7 @@ export default class SendRowWrapper extends Component {
{label} {showError && } + {!showError && showWarning && } {customLabelContent}
diff --git a/ui/app/components/send/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js index dbcc4ecd6..0f26dd55c 100644 --- a/ui/app/components/send/send-content/send-to-row/send-to-row.component.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import SendRowWrapper from '../send-row-wrapper/' import EnsInput from '../../../ens-input' -import { getToErrorObject } from './send-to-row.utils.js' +import { getToErrorObject, getToWarningObject } from './send-to-row.utils.js' export default class SendToRow extends Component { @@ -10,6 +10,7 @@ export default class SendToRow extends Component { closeToDropdown: PropTypes.func, hasHexData: PropTypes.bool.isRequired, inError: PropTypes.bool, + inWarning: PropTypes.bool, network: PropTypes.string, openToDropdown: PropTypes.func, selectedToken: PropTypes.object, @@ -20,6 +21,7 @@ export default class SendToRow extends Component { updateGas: PropTypes.func, updateSendTo: PropTypes.func, updateSendToError: PropTypes.func, + updateSendToWarning: PropTypes.func, scanQrCode: PropTypes.func, } @@ -27,11 +29,13 @@ export default class SendToRow extends Component { t: PropTypes.func, } - handleToChange (to, nickname = '', toError) { - const { hasHexData, updateSendTo, updateSendToError, updateGas, tokens, selectedToken } = this.props - const toErrorObject = getToErrorObject(to, toError, hasHexData, tokens, selectedToken) + handleToChange (to, nickname = '', toError, toWarning) { + const { hasHexData, updateSendTo, updateSendToError, updateGas, tokens, selectedToken, updateSendToWarning } = this.props + const toErrorObject = getToErrorObject(to, toError, hasHexData) + const toWarningObject = getToWarningObject(to, toWarning, tokens, selectedToken) updateSendTo(to, nickname) updateSendToError(toErrorObject) + updateSendToWarning(toWarningObject) if (toErrorObject.to === null) { updateGas({ to }) } @@ -41,6 +45,7 @@ export default class SendToRow extends Component { const { closeToDropdown, inError, + inWarning, network, openToDropdown, to, @@ -53,7 +58,9 @@ export default class SendToRow extends Component { errorType={'to'} label={`${this.context.t('to')}: `} showError={inError} - > + showWarning={inWarning} + warningType={'to'} + > this.props.scanQrCode()} accounts={toAccounts} @@ -62,7 +69,7 @@ export default class SendToRow extends Component { inError={inError} name={'address'} network={network} - onChange={({ toAddress, nickname, toError }) => this.handleToChange(toAddress, nickname, toError)} + onChange={({ toAddress, nickname, toError, toWarning }) => this.handleToChange(toAddress, nickname, toError, toWarning)} openDropdown={() => openToDropdown()} placeholder={this.context.t('recipientAddress')} to={to} diff --git a/ui/app/components/send/send-content/send-to-row/send-to-row.container.js b/ui/app/components/send/send-content/send-to-row/send-to-row.container.js index 945cdbaeb..fc6742df0 100644 --- a/ui/app/components/send/send-content/send-to-row/send-to-row.container.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.container.js @@ -10,12 +10,14 @@ import { getToDropdownOpen, getTokens, sendToIsInError, + sendToIsInWarning, } from './send-to-row.selectors.js' import { updateSendTo, } from '../../../../actions' import { updateSendErrors, + updateSendWarnings, openToDropdown, closeToDropdown, } from '../../../../ducks/send.duck' @@ -27,6 +29,7 @@ function mapStateToProps (state) { return { hasHexData: Boolean(getSendHexData(state)), inError: sendToIsInError(state), + inWarning: sendToIsInWarning(state), network: getCurrentNetwork(state), selectedToken: getSelectedToken(state), to: getSendTo(state), @@ -44,5 +47,8 @@ function mapDispatchToProps (dispatch) { updateSendToError: (toErrorObject) => { dispatch(updateSendErrors(toErrorObject)) }, + updateSendToWarning: (toWarningObject) => { + dispatch(updateSendWarnings(toWarningObject)) + }, } } diff --git a/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js b/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js index 982fd2e46..a6160d335 100644 --- a/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js @@ -2,6 +2,7 @@ const selectors = { getToDropdownOpen, getTokens, sendToIsInError, + sendToIsInWarning, } module.exports = selectors @@ -14,6 +15,10 @@ function sendToIsInError (state) { return Boolean(state.send.errors.to) } +function sendToIsInWarning (state) { + return Boolean(state.send.warnings.to) +} + function getTokens (state) { return state.metamask.tokens } diff --git a/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js b/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js index 7efd2a772..4a8add42e 100644 --- a/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js @@ -19,10 +19,17 @@ function getToErrorObject (to, toError = null, hasHexData = false, tokens = [], } else if (selectedToken && (ethUtil.toChecksumAddress(to) in contractMap || checkExistingAddresses(to, tokens))) { toError = KNOWN_RECIPIENT_ADDRESS_ERROR } - return { to: toError } } +function getToWarningObject (to, toWarning = null, tokens = [], selectedToken = null) { + if (selectedToken && (ethUtil.toChecksumAddress(to) in contractMap || checkExistingAddresses(to, tokens))) { + toWarning = KNOWN_RECIPIENT_ADDRESS_ERROR + } + return { to: toWarning } +} + module.exports = { getToErrorObject, + getToWarningObject, } diff --git a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js index 591229deb..d3732307f 100644 --- a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js @@ -9,6 +9,9 @@ const SendToRow = proxyquire('../send-to-row.component.js', { getToErrorObject: (to, toError) => ({ to: to === false ? null : `mockToErrorObject:${to}${toError}`, }), + getToWarningObject: (to, toWarning) => ({ + to: to === false ? null : `mockToWarningObject:${to}${toWarning}`, + }), }, }).default @@ -21,6 +24,7 @@ const propsMethodSpies = { updateGas: sinon.spy(), updateSendTo: sinon.spy(), updateSendToError: sinon.spy(), + updateSendToWarning: sinon.spy(), } sinon.spy(SendToRow.prototype, 'handleToChange') @@ -33,6 +37,7 @@ describe('SendToRow Component', function () { wrapper = shallow(, { context: { t: str => str + '_t' } }) instance = wrapper.instance() }) @@ -50,6 +56,7 @@ describe('SendToRow Component', function () { propsMethodSpies.openToDropdown.resetHistory() propsMethodSpies.updateSendTo.resetHistory() propsMethodSpies.updateSendToError.resetHistory() + propsMethodSpies.updateSendToWarning.resetHistory() SendToRow.prototype.handleToChange.resetHistory() }) @@ -75,6 +82,16 @@ describe('SendToRow Component', function () { ) }) + it('should call updateSendToWarning', () => { + assert.equal(propsMethodSpies.updateSendToWarning.callCount, 0) + instance.handleToChange('mockTo2', '', '', 'mockToWarning') + assert.equal(propsMethodSpies.updateSendToWarning.callCount, 1) + assert.deepEqual( + propsMethodSpies.updateSendToWarning.getCall(0).args, + [{ to: 'mockToWarningObject:mockTo2mockToWarning' }] + ) + }) + it('should not call updateGas if there is a to error', () => { assert.equal(propsMethodSpies.updateGas.callCount, 0) instance.handleToChange('mockTo2') @@ -138,11 +155,11 @@ describe('SendToRow Component', function () { openDropdown() assert.equal(propsMethodSpies.openToDropdown.callCount, 1) assert.equal(SendToRow.prototype.handleToChange.callCount, 0) - onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError' }) + onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError', toWarning: 'mockToWarning' }) assert.equal(SendToRow.prototype.handleToChange.callCount, 1) assert.deepEqual( SendToRow.prototype.handleToChange.getCall(0).args, - ['mockNewTo', 'mockNewNickname', 'mockToError'] + ['mockNewTo', 'mockNewNickname', 'mockToError', 'mockToWarning'] ) }) }) diff --git a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js index 95efdd7cc..aa09f01a9 100644 --- a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js @@ -12,6 +12,7 @@ const duckActionSpies = { closeToDropdown: sinon.spy(), openToDropdown: sinon.spy(), updateSendErrors: sinon.spy(), + updateSendWarnings: sinon.spy(), } proxyquire('../send-to-row.container.js', { @@ -32,6 +33,7 @@ proxyquire('../send-to-row.container.js', { './send-to-row.selectors.js': { getToDropdownOpen: (s) => `mockToDropdownOpen:${s}`, sendToIsInError: (s) => `mockInError:${s}`, + sendToIsInWarning: (s) => `mockInWarning:${s}`, getTokens: (s) => `mockTokens:${s}`, }, '../../../../actions': actionSpies, @@ -46,6 +48,7 @@ describe('send-to-row container', () => { assert.deepEqual(mapStateToProps('mockState'), { hasHexData: true, inError: 'mockInError:mockState', + inWarning: 'mockInWarning:mockState', network: 'mockNetwork:mockState', selectedToken: 'mockSelectedToken:mockState', to: 'mockTo:mockState', @@ -114,6 +117,18 @@ describe('send-to-row container', () => { }) }) + describe('updateSendToWarning()', () => { + it('should dispatch an action', () => { + mapDispatchToPropsObject.updateSendToWarning('mockToWarningObject') + assert(dispatchSpy.calledOnce) + assert(duckActionSpies.updateSendWarnings.calledOnce) + assert.equal( + duckActionSpies.updateSendWarnings.getCall(0).args[0], + 'mockToWarningObject' + ) + }) + }) + }) }) diff --git a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js index 8d4f26e15..f6abb26e6 100644 --- a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js @@ -19,6 +19,7 @@ const toRowUtils = proxyquire('../send-to-row.utils.js', { }) const { getToErrorObject, + getToWarningObject, } = toRowUtils describe('send-to-row utils', () => { @@ -78,4 +79,29 @@ describe('send-to-row utils', () => { }) }) + describe('getToWarningObject()', () => { + it('should return a known address recipient if to is truthy but part of state tokens', () => { + assert.deepEqual(getToWarningObject('0xabc123', undefined, [{'address': '0xabc123'}], {'address': '0xabc123'}), { + to: KNOWN_RECIPIENT_ADDRESS_ERROR, + }) + }) + + it('should null if to is truthy part of tokens but selectedToken falsy', () => { + assert.deepEqual(getToWarningObject('0xabc123', undefined, [{'address': '0xabc123'}]), { + to: null, + }) + }) + + it('should return a known address recipient if to is truthy but part of contract metadata', () => { + assert.deepEqual(getToWarningObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', undefined, [{'address': '0xabc123'}], {'address': '0xabc123'}), { + to: KNOWN_RECIPIENT_ADDRESS_ERROR, + }) + }) + it('should null if to is truthy part of contract metadata but selectedToken falsy', () => { + assert.deepEqual(getToWarningObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', undefined, [{'address': '0xabc123'}], {'address': '0xabc123'}), { + to: KNOWN_RECIPIENT_ADDRESS_ERROR, + }) + }) + }) + }) diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index 443c82af5..140da2ce9 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -51,6 +51,7 @@ const selectors = { getSendMaxModeState, getSendTo, getSendToAccounts, + getSendWarnings, getTokenBalance, getTokenExchangeRate, getUnapprovedTxs, @@ -268,6 +269,10 @@ function getSendToAccounts (state) { return Object.entries(allAccounts).map(([key, account]) => account) } +function getSendWarnings (state) { + return state.send.warnings +} + function getTokenBalance (state) { return state.metamask.send.tokenBalance } diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss index 19e840094..4372f275c 100644 --- a/ui/app/css/itcss/components/send.scss +++ b/ui/app/css/itcss/components/send.scss @@ -520,6 +520,13 @@ color: $red; } + &__warning { + font-size: 12px; + line-height: 12px; + left: 8px; + color: $orange; + } + &__error-border { color: $red; } diff --git a/ui/app/ducks/send.duck.js b/ui/app/ducks/send.duck.js index 4d212bd03..90e92140b 100644 --- a/ui/app/ducks/send.duck.js +++ b/ui/app/ducks/send.duck.js @@ -6,6 +6,7 @@ const CLOSE_FROM_DROPDOWN = 'metamask/send/CLOSE_FROM_DROPDOWN' const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN' const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN' const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS' +const UPDATE_SEND_WARNINGS = 'metamask/send/UPDATE_SEND_WARNINGS' const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE' const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP' const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP' @@ -16,6 +17,7 @@ const initState = { toDropdownOpen: false, gasButtonGroupShown: true, errors: {}, + warnings: {}, } // Reducer @@ -46,6 +48,13 @@ export default function reducer ({ send: sendState = initState }, action = {}) { ...action.value, }, }) + case UPDATE_SEND_WARNINGS: + return extend(newState, { + warnings: { + ...newState.warnings, + ...action.value, + }, + }) case SHOW_GAS_BUTTON_GROUP: return extend(newState, { gasButtonGroupShown: true, @@ -85,6 +94,13 @@ export function updateSendErrors (errorObject) { } } +export function updateSendWarnings (warningObject) { + return { + type: UPDATE_SEND_WARNINGS, + value: warningObject, + } +} + export function resetSendState () { return { type: RESET_SEND_STATE } } diff --git a/ui/app/ducks/tests/send-duck.test.js b/ui/app/ducks/tests/send-duck.test.js index ffd8bfb4b..43f51c631 100644 --- a/ui/app/ducks/tests/send-duck.test.js +++ b/ui/app/ducks/tests/send-duck.test.js @@ -6,6 +6,7 @@ import SendReducer, { updateSendErrors, showGasButtonGroup, hideGasButtonGroup, + updateSendWarnings, } from '../send.duck.js' describe('Send Duck', () => { @@ -19,12 +20,14 @@ describe('Send Duck', () => { toDropdownOpen: false, errors: {}, gasButtonGroupShown: true, + warnings: {}, } const OPEN_FROM_DROPDOWN = 'metamask/send/OPEN_FROM_DROPDOWN' const CLOSE_FROM_DROPDOWN = 'metamask/send/CLOSE_FROM_DROPDOWN' const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN' const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN' const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS' + const UPDATE_SEND_WARNINGS = 'metamask/send/UPDATE_SEND_WARNINGS' const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE' const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP' const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP' @@ -173,4 +176,11 @@ describe('Send Duck', () => { ) }) + describe('updateSendWarnings', () => { + assert.deepEqual( + updateSendWarnings('mockWarningObject'), + { type: UPDATE_SEND_WARNINGS, value: 'mockWarningObject' } + ) + }) + })