From 1d93d9a3852daf3842ef71c3775f41cbb79a3f00 Mon Sep 17 00:00:00 2001 From: Esteban MiƱo Date: Tue, 22 Jan 2019 16:05:59 -0300 Subject: Prevent send to token (#6051) * check contact metadata * check for tokens in state * tests * update tests * validation only if sending tokens * container test --- .../send-to-row/send-to-row.component.js | 6 ++++-- .../send-to-row/send-to-row.container.js | 4 ++++ .../send-to-row/send-to-row.selectors.js | 5 +++++ .../send-content/send-to-row/send-to-row.utils.js | 9 +++++++- .../tests/send-to-row-container.test.js | 4 ++++ .../tests/send-to-row-selectors.test.js | 12 +++++++++++ .../send-to-row/tests/send-to-row-utils.test.js | 24 ++++++++++++++++++++++ ui/app/components/send/send.constants.js | 2 ++ 8 files changed, 63 insertions(+), 3 deletions(-) (limited to 'ui') 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 ce5325314..dbcc4ecd6 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 @@ -12,9 +12,11 @@ export default class SendToRow extends Component { inError: PropTypes.bool, network: PropTypes.string, openToDropdown: PropTypes.func, + selectedToken: PropTypes.object, to: PropTypes.string, toAccounts: PropTypes.array, toDropdownOpen: PropTypes.bool, + tokens: PropTypes.array, updateGas: PropTypes.func, updateSendTo: PropTypes.func, updateSendToError: PropTypes.func, @@ -26,8 +28,8 @@ export default class SendToRow extends Component { } handleToChange (to, nickname = '', toError) { - const { hasHexData, updateSendTo, updateSendToError, updateGas } = this.props - const toErrorObject = getToErrorObject(to, toError, hasHexData) + const { hasHexData, updateSendTo, updateSendToError, updateGas, tokens, selectedToken } = this.props + const toErrorObject = getToErrorObject(to, toError, hasHexData, tokens, selectedToken) updateSendTo(to, nickname) updateSendToError(toErrorObject) if (toErrorObject.to === null) { 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 3ee188bad..945cdbaeb 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 @@ -1,12 +1,14 @@ import { connect } from 'react-redux' import { getCurrentNetwork, + getSelectedToken, getSendTo, getSendToAccounts, getSendHexData, } from '../../send.selectors.js' import { getToDropdownOpen, + getTokens, sendToIsInError, } from './send-to-row.selectors.js' import { @@ -26,9 +28,11 @@ function mapStateToProps (state) { hasHexData: Boolean(getSendHexData(state)), inError: sendToIsInError(state), network: getCurrentNetwork(state), + selectedToken: getSelectedToken(state), to: getSendTo(state), toAccounts: getSendToAccounts(state), toDropdownOpen: getToDropdownOpen(state), + tokens: getTokens(state), } } 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 8919014be..982fd2e46 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 @@ -1,5 +1,6 @@ const selectors = { getToDropdownOpen, + getTokens, sendToIsInError, } @@ -12,3 +13,7 @@ function getToDropdownOpen (state) { function sendToIsInError (state) { return Boolean(state.send.errors.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 0eeaa3a11..7efd2a772 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 @@ -1,16 +1,23 @@ const { REQUIRED_ERROR, INVALID_RECIPIENT_ADDRESS_ERROR, + KNOWN_RECIPIENT_ADDRESS_ERROR, } = require('../../send.constants') const { isValidAddress } = require('../../../../util') +import { checkExistingAddresses } from '../../../pages/add-token/util' -function getToErrorObject (to, toError = null, hasHexData = false) { +const ethUtil = require('ethereumjs-util') +const contractMap = require('eth-contract-metadata') + +function getToErrorObject (to, toError = null, hasHexData = false, tokens = [], selectedToken = null) { if (!to) { if (!hasHexData) { toError = REQUIRED_ERROR } } else if (!isValidAddress(to) && !toError) { toError = INVALID_RECIPIENT_ADDRESS_ERROR + } else if (selectedToken && (ethUtil.toChecksumAddress(to) in contractMap || checkExistingAddresses(to, tokens))) { + toError = KNOWN_RECIPIENT_ADDRESS_ERROR } return { to: toError } 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 dfce7652f..95efdd7cc 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 @@ -24,6 +24,7 @@ proxyquire('../send-to-row.container.js', { }, '../../send.selectors.js': { getCurrentNetwork: (s) => `mockNetwork:${s}`, + getSelectedToken: (s) => `mockSelectedToken:${s}`, getSendHexData: (s) => s, getSendTo: (s) => `mockTo:${s}`, getSendToAccounts: (s) => `mockToAccounts:${s}`, @@ -31,6 +32,7 @@ proxyquire('../send-to-row.container.js', { './send-to-row.selectors.js': { getToDropdownOpen: (s) => `mockToDropdownOpen:${s}`, sendToIsInError: (s) => `mockInError:${s}`, + getTokens: (s) => `mockTokens:${s}`, }, '../../../../actions': actionSpies, '../../../../ducks/send.duck': duckActionSpies, @@ -45,9 +47,11 @@ describe('send-to-row container', () => { hasHexData: true, inError: 'mockInError:mockState', network: 'mockNetwork:mockState', + selectedToken: 'mockSelectedToken:mockState', to: 'mockTo:mockState', toAccounts: 'mockToAccounts:mockState', toDropdownOpen: 'mockToDropdownOpen:mockState', + tokens: 'mockTokens:mockState', }) }) diff --git a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js index 122ad3265..0fa342d1e 100644 --- a/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js @@ -1,6 +1,7 @@ import assert from 'assert' import { getToDropdownOpen, + getTokens, sendToIsInError, } from '../send-to-row.selectors.js' @@ -44,4 +45,15 @@ describe('send-to-row selectors', () => { }) }) + describe('getTokens()', () => { + it('should return empty array if no tokens in state', () => { + const state = { + metamask: { + tokens: [], + }, + } + + assert.deepStrictEqual(getTokens(state), []) + }) + }) }) 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 c779aeb76..8d4f26e15 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 @@ -5,6 +5,7 @@ import sinon from 'sinon' import { REQUIRED_ERROR, INVALID_RECIPIENT_ADDRESS_ERROR, + KNOWN_RECIPIENT_ADDRESS_ERROR, } from '../../../send.constants' const stubs = { @@ -52,6 +53,29 @@ describe('send-to-row utils', () => { to: 'someExplicitError', }) }) + + it('should return a known address recipient if to is truthy but part of state tokens', () => { + assert.deepEqual(getToErrorObject('0xabc123', undefined, false, [{'address': '0xabc123'}], {'address': '0xabc123'}), { + to: KNOWN_RECIPIENT_ADDRESS_ERROR, + }) + }) + + it('should null if to is truthy part of tokens but selectedToken falsy', () => { + assert.deepEqual(getToErrorObject('0xabc123', undefined, false, [{'address': '0xabc123'}]), { + to: null, + }) + }) + + it('should return a known address recipient if to is truthy but part of contract metadata', () => { + assert.deepEqual(getToErrorObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', undefined, false, [{'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(getToErrorObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', undefined, false, [{'address': '0xabc123'}], {'address': '0xabc123'}), { + to: KNOWN_RECIPIENT_ADDRESS_ERROR, + }) + }) }) }) diff --git a/ui/app/components/send/send.constants.js b/ui/app/components/send/send.constants.js index 8acdf0641..e79734a54 100644 --- a/ui/app/components/send/send.constants.js +++ b/ui/app/components/send/send.constants.js @@ -27,6 +27,7 @@ const INSUFFICIENT_TOKENS_ERROR = 'insufficientTokens' const NEGATIVE_ETH_ERROR = 'negativeETH' const INVALID_RECIPIENT_ADDRESS_ERROR = 'invalidAddressRecipient' const REQUIRED_ERROR = 'required' +const KNOWN_RECIPIENT_ADDRESS_ERROR = 'knownAddressRecipient' const ONE_GWEI_IN_WEI_HEX = ethUtil.addHexPrefix(conversionUtil('0x1', { fromDenomination: 'GWEI', @@ -42,6 +43,7 @@ module.exports = { INSUFFICIENT_FUNDS_ERROR, INSUFFICIENT_TOKENS_ERROR, INVALID_RECIPIENT_ADDRESS_ERROR, + KNOWN_RECIPIENT_ADDRESS_ERROR, MIN_GAS_LIMIT_DEC, MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_DEC, -- cgit