1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Use send state for send flow token (#8695)

The chosen token in the `send` flow was set from one of two places:
`metamask.selectedTokenAddress` or `metamask.send.token`. The former is
used most of the time, but the latter is used for the 'Edit' button
shown in the upper-left of the confirmation UI.

The send flow will now exclusively use `metamask.send.token` for the
token state during the send flow. `metamask.selectedTokenAddress` is
now only used for the selected token state on the Home screen. This
simplifies the Redux state, as the send token is now in one place
instead of two, and `metamask.selectedTokenAddress` has only one
purpose.
This commit is contained in:
Mark Stacey 2020-05-29 14:46:10 -03:00 committed by GitHub
parent e481166052
commit ddaa492751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 282 additions and 279 deletions

View File

@ -28,7 +28,7 @@ import {
getCurrentCurrency, getCurrentCurrency,
getCurrentEthBalance, getCurrentEthBalance,
getIsMainnet, getIsMainnet,
getSelectedToken, getSendToken,
isEthereumNetwork, isEthereumNetwork,
getPreferences, getPreferences,
getBasicGasEstimateLoadingStatus, getBasicGasEstimateLoadingStatus,
@ -75,7 +75,7 @@ const mapStateToProps = (state, ownProps) => {
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state) const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
const gasEstimatesLoading = getGasEstimatesLoadingStatus(state) const gasEstimatesLoading = getGasEstimatesLoadingStatus(state)
const selectedToken = getSelectedToken(state) const sendToken = getSendToken(state)
// a "default" txParams is used during the send flow, since the transaction doesn't exist yet in that case // a "default" txParams is used during the send flow, since the transaction doesn't exist yet in that case
const txParams = selectedTransaction?.txParams const txParams = selectedTransaction?.txParams
@ -83,7 +83,7 @@ const mapStateToProps = (state, ownProps) => {
: { : {
gas: send.gasLimit || '0x5208', gas: send.gasLimit || '0x5208',
gasPrice: send.gasPrice || getFastPriceEstimateInHexWEI(state, true), gasPrice: send.gasPrice || getFastPriceEstimateInHexWEI(state, true),
value: selectedToken ? '0x0' : send.amount, value: sendToken ? '0x0' : send.amount,
} }
const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = txParams const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = txParams
@ -112,11 +112,11 @@ const mapStateToProps = (state, ownProps) => {
const isMainnet = getIsMainnet(state) const isMainnet = getIsMainnet(state)
const showFiat = Boolean(isMainnet || showFiatInTestnets) const showFiat = Boolean(isMainnet || showFiatInTestnets)
const isTokenSelected = Boolean(selectedToken) const isSendTokenSet = Boolean(sendToken)
const newTotalEth = maxModeOn && !isTokenSelected ? addHexWEIsToRenderableEth(balance, '0x0') : addHexWEIsToRenderableEth(value, customGasTotal) const newTotalEth = maxModeOn && !isSendTokenSet ? addHexWEIsToRenderableEth(balance, '0x0') : addHexWEIsToRenderableEth(value, customGasTotal)
const sendAmount = maxModeOn && !isTokenSelected ? subtractHexWEIsFromRenderableEth(balance, customGasTotal) : addHexWEIsToRenderableEth(value, '0x0') const sendAmount = maxModeOn && !isSendTokenSet ? subtractHexWEIsFromRenderableEth(balance, customGasTotal) : addHexWEIsToRenderableEth(value, '0x0')
const insufficientBalance = maxModeOn ? false : !isBalanceSufficient({ const insufficientBalance = maxModeOn ? false : !isBalanceSufficient({
amount: value, amount: value,
@ -167,7 +167,7 @@ const mapStateToProps = (state, ownProps) => {
gasEstimatesLoading, gasEstimatesLoading,
isMainnet, isMainnet,
isEthereumNetwork: isEthereumNetwork(state), isEthereumNetwork: isEthereumNetwork(state),
selectedToken: getSelectedToken(state), sendToken,
balance, balance,
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
} }
@ -223,7 +223,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
customGasPrice, customGasPrice,
customGasTotal, customGasTotal,
balance, balance,
selectedToken, sendToken,
tokenBalance, tokenBalance,
customGasLimit, customGasLimit,
transaction, transaction,
@ -274,7 +274,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
dispatchSetAmountToMax({ dispatchSetAmountToMax({
balance, balance,
gasTotal: customGasTotal, gasTotal: customGasTotal,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
} }

View File

@ -40,7 +40,7 @@ proxyquire('../gas-modal-page-container.container.js', {
getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${Object.keys(s).length}`, getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${Object.keys(s).length}`,
getDefaultActiveButtonIndex: (a, b) => a + b, getDefaultActiveButtonIndex: (a, b) => a + b,
getCurrentEthBalance: (state) => state.metamask.balance || '0x0', getCurrentEthBalance: (state) => state.metamask.balance || '0x0',
getSelectedToken: () => null, getSendToken: () => null,
getTokenBalance: (state) => state.metamask.send.tokenBalance || '0x0', getTokenBalance: (state) => state.metamask.send.tokenBalance || '0x0',
}, },
'../../../../store/actions': actionSpies, '../../../../store/actions': actionSpies,
@ -158,7 +158,7 @@ describe('gas-modal-page-container container', function () {
isEthereumNetwork: true, isEthereumNetwork: true,
isMainnet: true, isMainnet: true,
maxModeOn: false, maxModeOn: false,
selectedToken: null, sendToken: null,
tokenBalance: '0x0', tokenBalance: '0x0',
transaction: { transaction: {
id: 34, id: 34,

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import Button from '../../ui/button' import Button from '../../ui/button'
@ -11,8 +11,10 @@ import WalletOverview from './wallet-overview'
import { SEND_ROUTE } from '../../../helpers/constants/routes' import { SEND_ROUTE } from '../../../helpers/constants/routes'
import { useMetricEvent } from '../../../hooks/useMetricEvent' import { useMetricEvent } from '../../../hooks/useMetricEvent'
import { getAssetImages } from '../../../selectors/selectors' import { getAssetImages } from '../../../selectors/selectors'
import { updateSend } from '../../../store/actions'
const TokenOverview = ({ token }) => { const TokenOverview = ({ token }) => {
const dispatch = useDispatch()
const t = useContext(I18nContext) const t = useContext(I18nContext)
const sendTokenEvent = useMetricEvent({ const sendTokenEvent = useMetricEvent({
eventOpts: { eventOpts: {
@ -41,6 +43,7 @@ const TokenOverview = ({ token }) => {
className="token-overview__button" className="token-overview__button"
onClick={() => { onClick={() => {
sendTokenEvent() sendTokenEvent()
dispatch(updateSend({ token }))
history.push(SEND_ROUTE) history.push(SEND_ROUTE)
}} }}
> >

View File

@ -1,16 +1,14 @@
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { useCallback } from 'react' import { useCallback } from 'react'
import { setSelectedToken, showSidebar } from '../store/actions' import { showSidebar } from '../store/actions'
import { import {
fetchBasicGasAndTimeEstimates, fetchBasicGasAndTimeEstimates,
fetchGasEstimates, fetchGasEstimates,
setCustomGasPriceForRetry, setCustomGasPriceForRetry,
setCustomGasLimit, setCustomGasLimit,
} from '../ducks/gas/gas.duck' } from '../ducks/gas/gas.duck'
import { TOKEN_METHOD_TRANSFER } from '../helpers/constants/transactions'
import { increaseLastGasPrice } from '../helpers/utils/confirm-tx.util' import { increaseLastGasPrice } from '../helpers/utils/confirm-tx.util'
import { useMetricEvent } from './useMetricEvent' import { useMetricEvent } from './useMetricEvent'
import { useMethodData } from './useMethodData'
/** /**
@ -22,7 +20,6 @@ import { useMethodData } from './useMethodData'
export function useRetryTransaction (transactionGroup) { export function useRetryTransaction (transactionGroup) {
const { primaryTransaction, initialTransaction } = transactionGroup const { primaryTransaction, initialTransaction } = transactionGroup
const gasPrice = primaryTransaction.txParams.gasPrice const gasPrice = primaryTransaction.txParams.gasPrice
const methodData = useMethodData(primaryTransaction.txParams.data)
const trackMetricsEvent = useMetricEvent(({ const trackMetricsEvent = useMetricEvent(({
eventOpts: { eventOpts: {
category: 'Navigation', category: 'Navigation',
@ -32,8 +29,6 @@ export function useRetryTransaction (transactionGroup) {
})) }))
const dispatch = useDispatch() const dispatch = useDispatch()
const { name: methodName } = methodData || {}
const retryTransaction = useCallback(async (event) => { const retryTransaction = useCallback(async (event) => {
event.stopPropagation() event.stopPropagation()
@ -49,14 +44,7 @@ export function useRetryTransaction (transactionGroup) {
type: 'customize-gas', type: 'customize-gas',
props: { transaction }, props: { transaction },
})) }))
}, [dispatch, trackMetricsEvent, initialTransaction, gasPrice])
if (
methodName === TOKEN_METHOD_TRANSFER &&
initialTransaction.txParams.to
) {
dispatch(setSelectedToken(initialTransaction.txParams.to))
}
}, [dispatch, methodName, trackMetricsEvent, initialTransaction, gasPrice])
return retryTransaction return retryTransaction
} }

View File

@ -3,7 +3,7 @@ import { compose } from 'redux'
import { withRouter } from 'react-router-dom' import { withRouter } from 'react-router-dom'
import ConfirmSendToken from './confirm-send-token.component' import ConfirmSendToken from './confirm-send-token.component'
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck' import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'
import { setSelectedToken, updateSend, showSendTokenPage } from '../../store/actions' import { updateSend, showSendTokenPage } from '../../store/actions'
import { conversionUtil } from '../../helpers/utils/conversion-util' import { conversionUtil } from '../../helpers/utils/conversion-util'
import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors' import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors'
@ -38,7 +38,6 @@ const mapDispatchToProps = (dispatch) => {
toNumericBase: 'hex', toNumericBase: 'hex',
}) })
dispatch(setSelectedToken(tokenAddress))
dispatch(updateSend({ dispatch(updateSend({
from, from,
gasLimit, gasLimit,

View File

@ -24,9 +24,9 @@ export function getToErrorObject (to, hasHexData = false, _, __, network) {
return { to: toError } return { to: toError }
} }
export function getToWarningObject (to, tokens = [], selectedToken = null) { export function getToWarningObject (to, tokens = [], sendToken = null) {
let toWarning = null let toWarning = null
if (selectedToken && (ethUtil.toChecksumAddress(to) in contractMap || checkExistingAddresses(to, tokens))) { if (sendToken && (ethUtil.toChecksumAddress(to) in contractMap || checkExistingAddresses(to, tokens))) {
toWarning = KNOWN_RECIPIENT_ADDRESS_ERROR toWarning = KNOWN_RECIPIENT_ADDRESS_ERROR
} }
return { to: toWarning } return { to: toWarning }

View File

@ -55,7 +55,7 @@ describe('add-recipient utils', function () {
}) })
}) })
it('should null if to is truthy part of tokens but selectedToken falsy', function () { it('should null if to is truthy part of tokens but sendToken falsy', function () {
assert.deepEqual(getToErrorObject('0xabc123', false, [{ 'address': '0xabc123' }]), { assert.deepEqual(getToErrorObject('0xabc123', false, [{ 'address': '0xabc123' }]), {
to: null, to: null,
}) })
@ -66,7 +66,7 @@ describe('add-recipient utils', function () {
to: null, to: null,
}) })
}) })
it('should null if to is truthy part of contract metadata but selectedToken falsy', function () { it('should null if to is truthy part of contract metadata but sendToken falsy', function () {
assert.deepEqual(getToErrorObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', false, [{ 'address': '0xabc123' }], { 'address': '0xabc123' }), { assert.deepEqual(getToErrorObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', false, [{ 'address': '0xabc123' }], { 'address': '0xabc123' }), {
to: null, to: null,
}) })
@ -80,7 +80,7 @@ describe('add-recipient utils', function () {
}) })
}) })
it('should null if to is truthy part of tokens but selectedToken falsy', function () { it('should null if to is truthy part of tokens but sendToken falsy', function () {
assert.deepEqual(getToWarningObject('0xabc123', [{ 'address': '0xabc123' }]), { assert.deepEqual(getToWarningObject('0xabc123', [{ 'address': '0xabc123' }]), {
to: null, to: null,
}) })
@ -91,7 +91,7 @@ describe('add-recipient utils', function () {
to: KNOWN_RECIPIENT_ADDRESS_ERROR, to: KNOWN_RECIPIENT_ADDRESS_ERROR,
}) })
}) })
it('should null if to is truthy part of contract metadata but selectedToken falsy', function () { it('should null if to is truthy part of contract metadata but sendToken falsy', function () {
assert.deepEqual(getToWarningObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', [{ 'address': '0xabc123' }], { 'address': '0xabc123' }), { assert.deepEqual(getToWarningObject('0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', [{ 'address': '0xabc123' }], { 'address': '0xabc123' }), {
to: KNOWN_RECIPIENT_ADDRESS_ERROR, to: KNOWN_RECIPIENT_ADDRESS_ERROR,
}) })

View File

@ -11,7 +11,7 @@ export default class AmountMaxButton extends Component {
inError: PropTypes.bool, inError: PropTypes.bool,
gasTotal: PropTypes.string, gasTotal: PropTypes.string,
maxModeOn: PropTypes.bool, maxModeOn: PropTypes.bool,
selectedToken: PropTypes.object, sendToken: PropTypes.object,
setAmountToMax: PropTypes.func, setAmountToMax: PropTypes.func,
setMaxModeTo: PropTypes.func, setMaxModeTo: PropTypes.func,
tokenBalance: PropTypes.string, tokenBalance: PropTypes.string,
@ -27,7 +27,7 @@ export default class AmountMaxButton extends Component {
const { const {
balance, balance,
gasTotal, gasTotal,
selectedToken, sendToken,
setAmountToMax, setAmountToMax,
tokenBalance, tokenBalance,
} = this.props } = this.props
@ -35,7 +35,7 @@ export default class AmountMaxButton extends Component {
setAmountToMax({ setAmountToMax({
balance, balance,
gasTotal, gasTotal,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
} }

View File

@ -1,7 +1,7 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { import {
getGasTotal, getGasTotal,
getSelectedToken, getSendToken,
getSendFromBalance, getSendFromBalance,
getTokenBalance, getTokenBalance,
getSendMaxModeState, getSendMaxModeState,
@ -26,7 +26,7 @@ function mapStateToProps (state) {
buttonDataLoading: getBasicGasEstimateLoadingStatus(state), buttonDataLoading: getBasicGasEstimateLoadingStatus(state),
gasTotal: getGasTotal(state), gasTotal: getGasTotal(state),
maxModeOn: getSendMaxModeState(state), maxModeOn: getSendMaxModeState(state),
selectedToken: getSelectedToken(state), sendToken: getSendToken(state),
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
} }
} }

View File

@ -1,11 +1,11 @@
import { multiplyCurrencies, subtractCurrencies } from '../../../../../helpers/utils/conversion-util' import { multiplyCurrencies, subtractCurrencies } from '../../../../../helpers/utils/conversion-util'
import ethUtil from 'ethereumjs-util' import ethUtil from 'ethereumjs-util'
export function calcMaxAmount ({ balance, gasTotal, selectedToken, tokenBalance }) { export function calcMaxAmount ({ balance, gasTotal, sendToken, tokenBalance }) {
const { decimals } = selectedToken || {} const { decimals } = sendToken || {}
const multiplier = Math.pow(10, Number(decimals || 0)) const multiplier = Math.pow(10, Number(decimals || 0))
return selectedToken return sendToken
? multiplyCurrencies( ? multiplyCurrencies(
tokenBalance, tokenBalance,
multiplier, multiplier,

View File

@ -25,7 +25,7 @@ describe('AmountMaxButton Component', function () {
balance="mockBalance" balance="mockBalance"
gasTotal="mockGasTotal" gasTotal="mockGasTotal"
maxModeOn={false} maxModeOn={false}
selectedToken={ { address: 'mockTokenAddress' } } sendToken={ { address: 'mockTokenAddress' } }
setAmountToMax={propsMethodSpies.setAmountToMax} setAmountToMax={propsMethodSpies.setAmountToMax}
setMaxModeTo={propsMethodSpies.setMaxModeTo} setMaxModeTo={propsMethodSpies.setMaxModeTo}
tokenBalance="mockTokenBalance" tokenBalance="mockTokenBalance"
@ -60,7 +60,7 @@ describe('AmountMaxButton Component', function () {
[{ [{
balance: 'mockBalance', balance: 'mockBalance',
gasTotal: 'mockGasTotal', gasTotal: 'mockGasTotal',
selectedToken: { address: 'mockTokenAddress' }, sendToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance', tokenBalance: 'mockTokenBalance',
}] }]
) )

View File

@ -23,7 +23,7 @@ proxyquire('../amount-max-button.container.js', {
}, },
'../../../../../selectors': { '../../../../../selectors': {
getGasTotal: (s) => `mockGasTotal:${s}`, getGasTotal: (s) => `mockGasTotal:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`, getSendToken: (s) => `mockSendToken:${s}`,
getSendFromBalance: (s) => `mockBalance:${s}`, getSendFromBalance: (s) => `mockBalance:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`, getTokenBalance: (s) => `mockTokenBalance:${s}`,
getSendMaxModeState: (s) => `mockMaxModeOn:${s}`, getSendMaxModeState: (s) => `mockMaxModeOn:${s}`,
@ -44,7 +44,7 @@ describe('amount-max-button container', function () {
buttonDataLoading: 'mockButtonDataLoading:mockState', buttonDataLoading: 'mockButtonDataLoading:mockState',
gasTotal: 'mockGasTotal:mockState', gasTotal: 'mockGasTotal:mockState',
maxModeOn: 'mockMaxModeOn:mockState', maxModeOn: 'mockMaxModeOn:mockState',
selectedToken: 'mockSelectedToken:mockState', sendToken: 'mockSendToken:mockState',
tokenBalance: 'mockTokenBalance:mockState', tokenBalance: 'mockTokenBalance:mockState',
}) })
}) })

View File

@ -6,17 +6,17 @@ import {
describe('amount-max-button utils', function () { describe('amount-max-button utils', function () {
describe('calcMaxAmount()', function () { describe('calcMaxAmount()', function () {
it('should calculate the correct amount when no selectedToken defined', function () { it('should calculate the correct amount when no sendToken defined', function () {
assert.deepEqual(calcMaxAmount({ assert.deepEqual(calcMaxAmount({
balance: 'ffffff', balance: 'ffffff',
gasTotal: 'ff', gasTotal: 'ff',
selectedToken: false, sendToken: false,
}), 'ffff00') }), 'ffff00')
}) })
it('should calculate the correct amount when a selectedToken is defined', function () { it('should calculate the correct amount when a sendToken is defined', function () {
assert.deepEqual(calcMaxAmount({ assert.deepEqual(calcMaxAmount({
selectedToken: { sendToken: {
decimals: 10, decimals: 10,
}, },
tokenBalance: '64', tokenBalance: '64',

View File

@ -15,7 +15,7 @@ export default class SendAmountRow extends Component {
gasTotal: PropTypes.string, gasTotal: PropTypes.string,
inError: PropTypes.bool, inError: PropTypes.bool,
primaryCurrency: PropTypes.string, primaryCurrency: PropTypes.string,
selectedToken: PropTypes.object, sendToken: PropTypes.object,
setMaxModeTo: PropTypes.func, setMaxModeTo: PropTypes.func,
tokenBalance: PropTypes.string, tokenBalance: PropTypes.string,
updateGasFeeError: PropTypes.func, updateGasFeeError: PropTypes.func,
@ -31,9 +31,9 @@ export default class SendAmountRow extends Component {
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { maxModeOn: prevMaxModeOn, gasTotal: prevGasTotal } = prevProps const { maxModeOn: prevMaxModeOn, gasTotal: prevGasTotal } = prevProps
const { maxModeOn, amount, gasTotal, selectedToken } = this.props const { maxModeOn, amount, gasTotal, sendToken } = this.props
if (maxModeOn && selectedToken && !prevMaxModeOn) { if (maxModeOn && sendToken && !prevMaxModeOn) {
this.updateGas(amount) this.updateGas(amount)
} }
@ -50,7 +50,7 @@ export default class SendAmountRow extends Component {
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
updateGasFeeError, updateGasFeeError,
updateSendAmountError, updateSendAmountError,
@ -62,17 +62,17 @@ export default class SendAmountRow extends Component {
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
if (selectedToken) { if (sendToken) {
updateGasFeeError({ updateGasFeeError({
balance, balance,
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
} }
@ -86,9 +86,9 @@ export default class SendAmountRow extends Component {
} }
updateGas (amount) { updateGas (amount) {
const { selectedToken, updateGas } = this.props const { sendToken, updateGas } = this.props
if (selectedToken) { if (sendToken) {
updateGas({ amount }) updateGas({ amount })
} }
} }
@ -100,14 +100,14 @@ export default class SendAmountRow extends Component {
} }
renderInput () { renderInput () {
const { amount, inError, selectedToken } = this.props const { amount, inError, sendToken } = this.props
return selectedToken ? return sendToken ?
( (
<UserPreferencedTokenInput <UserPreferencedTokenInput
error={inError} error={inError}
onChange={this.handleChange} onChange={this.handleChange}
token={selectedToken} token={sendToken}
value={amount} value={amount}
/> />
) )

View File

@ -3,7 +3,7 @@ import {
getConversionRate, getConversionRate,
getGasTotal, getGasTotal,
getPrimaryCurrency, getPrimaryCurrency,
getSelectedToken, getSendToken,
getSendAmount, getSendAmount,
getSendFromBalance, getSendFromBalance,
getTokenBalance, getTokenBalance,
@ -30,7 +30,7 @@ function mapStateToProps (state) {
gasTotal: getGasTotal(state), gasTotal: getGasTotal(state),
inError: sendAmountIsInError(state), inError: sendAmountIsInError(state),
primaryCurrency: getPrimaryCurrency(state), primaryCurrency: getPrimaryCurrency(state),
selectedToken: getSelectedToken(state), sendToken: getSendToken(state),
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
maxModeOn: getSendMaxModeState(state), maxModeOn: getSendMaxModeState(state),
} }

View File

@ -23,12 +23,12 @@ describe('SendAmountRow Component', function () {
conversionRate: 7, conversionRate: 7,
gasTotal: 'mockGasTotal', gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency', primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress' }, sendToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance', tokenBalance: 'mockTokenBalance',
})) }))
}) })
it('should call updateGasFeeError if selectedToken is truthy', function () { it('should call updateGasFeeError if sendToken is truthy', function () {
const { instance, propsMethodSpies: { updateGasFeeError } } = shallowRenderSendAmountRow() const { instance, propsMethodSpies: { updateGasFeeError } } = shallowRenderSendAmountRow()
assert.equal(updateGasFeeError.callCount, 0) assert.equal(updateGasFeeError.callCount, 0)
@ -40,15 +40,15 @@ describe('SendAmountRow Component', function () {
conversionRate: 7, conversionRate: 7,
gasTotal: 'mockGasTotal', gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency', primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress' }, sendToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance', tokenBalance: 'mockTokenBalance',
})) }))
}) })
it('should call not updateGasFeeError if selectedToken is falsey', function () { it('should call not updateGasFeeError if sendToken is falsey', function () {
const { wrapper, instance, propsMethodSpies: { updateGasFeeError } } = shallowRenderSendAmountRow() const { wrapper, instance, propsMethodSpies: { updateGasFeeError } } = shallowRenderSendAmountRow()
wrapper.setProps({ selectedToken: null }) wrapper.setProps({ sendToken: null })
assert.equal(updateGasFeeError.callCount, 0) assert.equal(updateGasFeeError.callCount, 0)
@ -152,7 +152,7 @@ function shallowRenderSendAmountRow () {
gasTotal="mockGasTotal" gasTotal="mockGasTotal"
inError={false} inError={false}
primaryCurrency="mockPrimaryCurrency" primaryCurrency="mockPrimaryCurrency"
selectedToken={ { address: 'mockTokenAddress' } } sendToken={ { address: 'mockTokenAddress' } }
setMaxModeTo={setMaxModeTo} setMaxModeTo={setMaxModeTo}
tokenBalance="mockTokenBalance" tokenBalance="mockTokenBalance"
updateGasFeeError={updateGasFeeError} updateGasFeeError={updateGasFeeError}

View File

@ -17,8 +17,8 @@ export default class SendAssetRow extends Component {
).isRequired, ).isRequired,
accounts: PropTypes.object.isRequired, accounts: PropTypes.object.isRequired,
selectedAddress: PropTypes.string.isRequired, selectedAddress: PropTypes.string.isRequired,
selectedTokenAddress: PropTypes.string, sendTokenAddress: PropTypes.string,
setSelectedToken: PropTypes.func.isRequired, setSendToken: PropTypes.func.isRequired,
} }
static contextTypes = { static contextTypes = {
@ -34,7 +34,7 @@ export default class SendAssetRow extends Component {
closeDropdown = () => this.setState({ isShowingDropdown: false }) closeDropdown = () => this.setState({ isShowingDropdown: false })
selectToken = (address) => { selectToken = (token) => {
this.setState({ this.setState({
isShowingDropdown: false, isShowingDropdown: false,
}, () => { }, () => {
@ -45,10 +45,10 @@ export default class SendAssetRow extends Component {
name: 'User clicks "Assets" dropdown', name: 'User clicks "Assets" dropdown',
}, },
customVariables: { customVariables: {
assetSelected: address ? 'ERC20' : 'ETH', assetSelected: token ? 'ERC20' : 'ETH',
}, },
}) })
this.props.setSelectedToken(address) this.props.setSendToken(token)
}) })
} }
@ -58,16 +58,16 @@ export default class SendAssetRow extends Component {
return ( return (
<SendRowWrapper label={`${t('asset')}:`}> <SendRowWrapper label={`${t('asset')}:`}>
<div className="send-v2__asset-dropdown"> <div className="send-v2__asset-dropdown">
{ this.renderSelectedToken() } { this.renderSendToken() }
{ this.props.tokens.length > 0 ? this.renderAssetDropdown() : null } { this.props.tokens.length > 0 ? this.renderAssetDropdown() : null }
</div> </div>
</SendRowWrapper> </SendRowWrapper>
) )
} }
renderSelectedToken () { renderSendToken () {
const { selectedTokenAddress } = this.props const { sendTokenAddress } = this.props
const token = this.props.tokens.find(({ address }) => address === selectedTokenAddress) const token = this.props.tokens.find(({ address }) => address === sendTokenAddress)
return ( return (
<div <div
className="send-v2__asset-dropdown__input-wrapper" className="send-v2__asset-dropdown__input-wrapper"
@ -133,7 +133,7 @@ export default class SendAssetRow extends Component {
<div <div
key={address} key={address}
className="send-v2__asset-dropdown__asset" className="send-v2__asset-dropdown__asset"
onClick={() => this.selectToken(address)} onClick={() => this.selectToken(token)}
> >
<div className="send-v2__asset-dropdown__asset-icon"> <div className="send-v2__asset-dropdown__asset-icon">
<Identicon address={address} diameter={36} /> <Identicon address={address} diameter={36} />

View File

@ -1,20 +1,20 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import SendAssetRow from './send-asset-row.component' import SendAssetRow from './send-asset-row.component'
import { getMetaMaskAccounts } from '../../../../selectors' import { getMetaMaskAccounts, getSendTokenAddress } from '../../../../selectors'
import { setSelectedToken } from '../../../../store/actions' import { updateSend } from '../../../../store/actions'
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
tokens: state.metamask.tokens, tokens: state.metamask.tokens,
selectedAddress: state.metamask.selectedAddress, selectedAddress: state.metamask.selectedAddress,
selectedTokenAddress: state.metamask.selectedTokenAddress, sendTokenAddress: getSendTokenAddress(state),
accounts: getMetaMaskAccounts(state), accounts: getMetaMaskAccounts(state),
} }
} }
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
setSelectedToken: (address) => dispatch(setSelectedToken(address)), setSendToken: (token) => dispatch(updateSend({ token })),
} }
} }

View File

@ -14,7 +14,7 @@ export default class SendGasRow extends Component {
gasTotal: PropTypes.string, gasTotal: PropTypes.string,
maxModeOn: PropTypes.bool, maxModeOn: PropTypes.bool,
showCustomizeGasModal: PropTypes.func, showCustomizeGasModal: PropTypes.func,
selectedToken: PropTypes.object, sendToken: PropTypes.object,
setAmountToMax: PropTypes.func, setAmountToMax: PropTypes.func,
setGasPrice: PropTypes.func, setGasPrice: PropTypes.func,
setGasLimit: PropTypes.func, setGasLimit: PropTypes.func,
@ -59,7 +59,7 @@ export default class SendGasRow extends Component {
const { const {
balance, balance,
gasTotal, gasTotal,
selectedToken, sendToken,
setAmountToMax, setAmountToMax,
tokenBalance, tokenBalance,
} = this.props } = this.props
@ -67,7 +67,7 @@ export default class SendGasRow extends Component {
setAmountToMax({ setAmountToMax({
balance, balance,
gasTotal, gasTotal,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
} }

View File

@ -13,7 +13,7 @@ import {
getGasButtonGroupShown, getGasButtonGroupShown,
getAdvancedInlineGasShown, getAdvancedInlineGasShown,
getCurrentEthBalance, getCurrentEthBalance,
getSelectedToken, getSendToken,
getBasicGasEstimateLoadingStatus, getBasicGasEstimateLoadingStatus,
getRenderableEstimateDataForSmallButtonsFromGWEI, getRenderableEstimateDataForSmallButtonsFromGWEI,
getDefaultActiveButtonIndex, getDefaultActiveButtonIndex,
@ -49,7 +49,7 @@ function mapStateToProps (state) {
const balance = getCurrentEthBalance(state) const balance = getCurrentEthBalance(state)
const insufficientBalance = !isBalanceSufficient({ const insufficientBalance = !isBalanceSufficient({
amount: getSelectedToken(state) ? '0x0' : getSendAmount(state), amount: getSendToken(state) ? '0x0' : getSendAmount(state),
gasTotal, gasTotal,
balance, balance,
conversionRate, conversionRate,
@ -72,7 +72,7 @@ function mapStateToProps (state) {
gasLimit, gasLimit,
insufficientBalance, insufficientBalance,
maxModeOn: getSendMaxModeState(state), maxModeOn: getSendMaxModeState(state),
selectedToken: getSelectedToken(state), sendToken: getSendToken(state),
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
} }
} }

View File

@ -17,7 +17,7 @@ export default class SendFooter extends Component {
gasTotal: PropTypes.string, gasTotal: PropTypes.string,
history: PropTypes.object, history: PropTypes.object,
inError: PropTypes.bool, inError: PropTypes.bool,
selectedToken: PropTypes.object, sendToken: PropTypes.object,
sign: PropTypes.func, sign: PropTypes.func,
to: PropTypes.string, to: PropTypes.string,
toAccounts: PropTypes.array, toAccounts: PropTypes.array,
@ -49,7 +49,7 @@ export default class SendFooter extends Component {
from: { address: from }, from: { address: from },
gasLimit: gas, gasLimit: gas,
gasPrice, gasPrice,
selectedToken, sendToken,
sign, sign,
to, to,
unapprovedTxs, unapprovedTxs,
@ -78,11 +78,11 @@ export default class SendFooter extends Component {
from, from,
gas, gas,
gasPrice, gasPrice,
selectedToken, sendToken,
to, to,
unapprovedTxs, unapprovedTxs,
}) })
: sign({ data, selectedToken, to, amount, from, gas, gasPrice }) : sign({ data, sendToken, to, amount, from, gas, gasPrice })
Promise.resolve(promise) Promise.resolve(promise)
.then(() => { .then(() => {
@ -101,8 +101,8 @@ export default class SendFooter extends Component {
} }
formShouldBeDisabled () { formShouldBeDisabled () {
const { data, inError, selectedToken, tokenBalance, gasTotal, to, gasLimit, gasIsLoading } = this.props const { data, inError, sendToken, tokenBalance, gasTotal, to, gasLimit, gasIsLoading } = this.props
const missingTokenBalance = selectedToken && !tokenBalance const missingTokenBalance = sendToken && !tokenBalance
const gasLimitTooLow = gasLimit < 5208 // 5208 is hex value of 21000, minimum gas limit const gasLimitTooLow = gasLimit < 5208 // 5208 is hex value of 21000, minimum gas limit
const shouldBeDisabled = inError || !gasTotal || missingTokenBalance || !(data || to) || gasLimitTooLow || gasIsLoading const shouldBeDisabled = inError || !gasTotal || missingTokenBalance || !(data || to) || gasLimitTooLow || gasIsLoading
return shouldBeDisabled return shouldBeDisabled

View File

@ -11,7 +11,7 @@ import {
getGasLimit, getGasLimit,
getGasPrice, getGasPrice,
getGasTotal, getGasTotal,
getSelectedToken, getSendToken,
getSendAmount, getSendAmount,
getSendEditingTransactionId, getSendEditingTransactionId,
getSendFromObject, getSendFromObject,
@ -54,7 +54,7 @@ function mapStateToProps (state) {
gasPrice: getGasPrice(state), gasPrice: getGasPrice(state),
gasTotal: getGasTotal(state), gasTotal: getGasTotal(state),
inError: isSendFormInError(state), inError: isSendFormInError(state),
selectedToken: getSelectedToken(state), sendToken: getSendToken(state),
to: getSendTo(state), to: getSendTo(state),
toAccounts: getSendToAccounts(state), toAccounts: getSendToAccounts(state),
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
@ -68,19 +68,19 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
clearSend: () => dispatch(clearSend()), clearSend: () => dispatch(clearSend()),
sign: ({ selectedToken, to, amount, from, gas, gasPrice, data }) => { sign: ({ sendToken, to, amount, from, gas, gasPrice, data }) => {
const txParams = constructTxParams({ const txParams = constructTxParams({
amount, amount,
data, data,
from, from,
gas, gas,
gasPrice, gasPrice,
selectedToken, sendToken,
to, to,
}) })
selectedToken sendToken
? dispatch(signTokenTx(selectedToken.address, to, amount, txParams)) ? dispatch(signTokenTx(sendToken.address, to, amount, txParams))
: dispatch(signTx(txParams)) : dispatch(signTx(txParams))
}, },
update: ({ update: ({
@ -90,7 +90,7 @@ function mapDispatchToProps (dispatch) {
from, from,
gas, gas,
gasPrice, gasPrice,
selectedToken, sendToken,
to, to,
unapprovedTxs, unapprovedTxs,
}) => { }) => {
@ -101,7 +101,7 @@ function mapDispatchToProps (dispatch) {
from, from,
gas, gas,
gasPrice, gasPrice,
selectedToken, sendToken,
to, to,
unapprovedTxs, unapprovedTxs,
}) })

View File

@ -8,7 +8,7 @@ export function addHexPrefixToObjectValues (obj) {
}, {}) }, {})
} }
export function constructTxParams ({ selectedToken, data, to, amount, from, gas, gasPrice }) { export function constructTxParams ({ sendToken, data, to, amount, from, gas, gasPrice }) {
const txParams = { const txParams = {
data, data,
from, from,
@ -17,7 +17,7 @@ export function constructTxParams ({ selectedToken, data, to, amount, from, gas,
gasPrice, gasPrice,
} }
if (!selectedToken) { if (!sendToken) {
txParams.value = amount txParams.value = amount
txParams.to = to txParams.to = to
} }
@ -32,7 +32,7 @@ export function constructUpdatedTx ({
from, from,
gas, gas,
gasPrice, gasPrice,
selectedToken, sendToken,
to, to,
unapprovedTxs, unapprovedTxs,
}) { }) {
@ -54,7 +54,7 @@ export function constructUpdatedTx ({
), ),
} }
if (selectedToken) { if (sendToken) {
const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call( const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]), ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
(x) => ('00' + x.toString(16)).slice(-2) (x) => ('00' + x.toString(16)).slice(-2)
@ -62,7 +62,7 @@ export function constructUpdatedTx ({
Object.assign(editingTx.txParams, addHexPrefixToObjectValues({ Object.assign(editingTx.txParams, addHexPrefixToObjectValues({
value: '0', value: '0',
to: selectedToken.address, to: sendToken.address,
data, data,
})) }))
} }

View File

@ -40,7 +40,7 @@ describe('SendFooter Component', function () {
gasTotal="mockGasTotal" gasTotal="mockGasTotal"
history={historySpies} history={historySpies}
inError={false} inError={false}
selectedToken={{ mockProp: 'mockSelectedTokenProp' }} sendToken={{ mockProp: 'mockSendTokenProp' }}
sign={propsMethodSpies.sign} sign={propsMethodSpies.sign}
to="mockTo" to="mockTo"
toAccounts={['mockAccount']} toAccounts={['mockAccount']}
@ -103,8 +103,8 @@ describe('SendFooter Component', function () {
expectedResult: true, expectedResult: true,
gasIsLoading: false, gasIsLoading: false,
}, },
'should return true if selectedToken is truthy and tokenBalance is falsy': { 'should return true if sendToken is truthy and tokenBalance is falsy': {
selectedToken: { mockProp: 'mockSelectedTokenProp' }, sendToken: { mockProp: 'mockSendTokenProp' },
tokenBalance: '', tokenBalance: '',
expectedResult: true, expectedResult: true,
gasIsLoading: false, gasIsLoading: false,
@ -112,7 +112,7 @@ describe('SendFooter Component', function () {
'should return true if gasIsLoading is truthy but all other params are falsy': { 'should return true if gasIsLoading is truthy but all other params are falsy': {
inError: false, inError: false,
gasTotal: '', gasTotal: '',
selectedToken: null, sendToken: null,
tokenBalance: '', tokenBalance: '',
expectedResult: true, expectedResult: true,
gasIsLoading: true, gasIsLoading: true,
@ -120,7 +120,7 @@ describe('SendFooter Component', function () {
'should return false if inError is false and all other params are truthy': { 'should return false if inError is false and all other params are truthy': {
inError: false, inError: false,
gasTotal: '0x123', gasTotal: '0x123',
selectedToken: { mockProp: 'mockSelectedTokenProp' }, sendToken: { mockProp: 'mockSendTokenProp' },
tokenBalance: '123', tokenBalance: '123',
expectedResult: false, expectedResult: false,
gasIsLoading: false, gasIsLoading: false,
@ -157,7 +157,7 @@ describe('SendFooter Component', function () {
from: 'mockAddress', from: 'mockAddress',
gas: 'mockGasLimit', gas: 'mockGasLimit',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedToken: { mockProp: 'mockSelectedTokenProp' }, sendToken: { mockProp: 'mockSendTokenProp' },
to: 'mockTo', to: 'mockTo',
unapprovedTxs: {}, unapprovedTxs: {},
} }
@ -180,7 +180,7 @@ describe('SendFooter Component', function () {
from: 'mockAddress', from: 'mockAddress',
gas: 'mockGasLimit', gas: 'mockGasLimit',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedToken: { mockProp: 'mockSelectedTokenProp' }, sendToken: { mockProp: 'mockSendTokenProp' },
to: 'mockTo', to: 'mockTo',
} }
) )
@ -214,7 +214,7 @@ describe('SendFooter Component', function () {
gasTotal="mockGasTotal" gasTotal="mockGasTotal"
history={historySpies} history={historySpies}
inError={false} inError={false}
selectedToken={{ mockProp: 'mockSelectedTokenProp' }} sendToken={{ mockProp: 'mockSendTokenProp' }}
sign={propsMethodSpies.sign} sign={propsMethodSpies.sign}
to="mockTo" to="mockTo"
toAccounts={['mockAccount']} toAccounts={['mockAccount']}

View File

@ -31,7 +31,7 @@ proxyquire('../send-footer.container.js', {
getGasLimit: (s) => `mockGasLimit:${s}`, getGasLimit: (s) => `mockGasLimit:${s}`,
getGasPrice: (s) => `mockGasPrice:${s}`, getGasPrice: (s) => `mockGasPrice:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`, getGasTotal: (s) => `mockGasTotal:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`, getSendToken: (s) => `mockSendToken:${s}`,
getSendAmount: (s) => `mockAmount:${s}`, getSendAmount: (s) => `mockAmount:${s}`,
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`, getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
getSendFromObject: (s) => `mockFromObject:${s}`, getSendFromObject: (s) => `mockFromObject:${s}`,
@ -69,9 +69,9 @@ describe('send-footer container', function () {
}) })
describe('sign()', function () { describe('sign()', function () {
it('should dispatch a signTokenTx action if selectedToken is defined', function () { it('should dispatch a signTokenTx action if sendToken is defined', function () {
mapDispatchToPropsObject.sign({ mapDispatchToPropsObject.sign({
selectedToken: { sendToken: {
address: '0xabc', address: '0xabc',
}, },
to: 'mockTo', to: 'mockTo',
@ -85,7 +85,7 @@ describe('send-footer container', function () {
utilsStubs.constructTxParams.getCall(0).args[0], utilsStubs.constructTxParams.getCall(0).args[0],
{ {
data: undefined, data: undefined,
selectedToken: { sendToken: {
address: '0xabc', address: '0xabc',
}, },
to: 'mockTo', to: 'mockTo',
@ -101,7 +101,7 @@ describe('send-footer container', function () {
) )
}) })
it('should dispatch a sign action if selectedToken is not defined', function () { it('should dispatch a sign action if sendToken is not defined', function () {
utilsStubs.constructTxParams.resetHistory() utilsStubs.constructTxParams.resetHistory()
mapDispatchToPropsObject.sign({ mapDispatchToPropsObject.sign({
to: 'mockTo', to: 'mockTo',
@ -115,7 +115,7 @@ describe('send-footer container', function () {
utilsStubs.constructTxParams.getCall(0).args[0], utilsStubs.constructTxParams.getCall(0).args[0],
{ {
data: undefined, data: undefined,
selectedToken: undefined, sendToken: undefined,
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockFrom', from: 'mockFrom',
@ -139,7 +139,7 @@ describe('send-footer container', function () {
gas: 'mockGas', gas: 'mockGas',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
editingTransactionId: 'mockEditingTransactionId', editingTransactionId: 'mockEditingTransactionId',
selectedToken: 'mockSelectedToken', sendToken: { address: 'mockAddress' },
unapprovedTxs: 'mockUnapprovedTxs', unapprovedTxs: 'mockUnapprovedTxs',
}) })
assert(dispatchSpy.calledOnce) assert(dispatchSpy.calledOnce)
@ -153,7 +153,7 @@ describe('send-footer container', function () {
gas: 'mockGas', gas: 'mockGas',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
editingTransactionId: 'mockEditingTransactionId', editingTransactionId: 'mockEditingTransactionId',
selectedToken: 'mockSelectedToken', sendToken: { address: 'mockAddress' },
unapprovedTxs: 'mockUnapprovedTxs', unapprovedTxs: 'mockUnapprovedTxs',
} }
) )

View File

@ -69,7 +69,7 @@ describe('send-footer utils', function () {
assert.deepEqual( assert.deepEqual(
constructTxParams({ constructTxParams({
data: 'someData', data: 'someData',
selectedToken: false, sendToken: undefined,
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockFrom', from: 'mockFrom',
@ -87,10 +87,10 @@ describe('send-footer utils', function () {
) )
}) })
it('should return a new txParams object with value and to properties if there is no selectedToken', function () { it('should return a new txParams object with value and to properties if there is no sendToken', function () {
assert.deepEqual( assert.deepEqual(
constructTxParams({ constructTxParams({
selectedToken: false, sendToken: undefined,
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockFrom', from: 'mockFrom',
@ -108,10 +108,10 @@ describe('send-footer utils', function () {
) )
}) })
it('should return a new txParams object without a to property and a 0 value if there is a selectedToken', function () { it('should return a new txParams object without a to property and a 0 value if there is a sendToken', function () {
assert.deepEqual( assert.deepEqual(
constructTxParams({ constructTxParams({
selectedToken: true, sendToken: { address: '0x0' },
to: 'mockTo', to: 'mockTo',
amount: 'mockAmount', amount: 'mockAmount',
from: 'mockFrom', from: 'mockFrom',
@ -137,7 +137,7 @@ describe('send-footer utils', function () {
from: 'mockFrom', from: 'mockFrom',
gas: 'mockGas', gas: 'mockGas',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedToken: false, sendToken: false,
to: 'mockTo', to: 'mockTo',
unapprovedTxs: { unapprovedTxs: {
'0x123': {}, '0x123': {},
@ -169,7 +169,7 @@ describe('send-footer utils', function () {
from: 'mockFrom', from: 'mockFrom',
gas: 'mockGas', gas: 'mockGas',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedToken: false, sendToken: false,
to: 'mockTo', to: 'mockTo',
unapprovedTxs: { unapprovedTxs: {
'0x123': {}, '0x123': {},
@ -196,14 +196,14 @@ describe('send-footer utils', function () {
}) })
}) })
it('should have token property values if selectedToken is truthy', function () { it('should have token property values if sendToken is truthy', function () {
const result = constructUpdatedTx({ const result = constructUpdatedTx({
amount: 'mockAmount', amount: 'mockAmount',
editingTransactionId: '0x456', editingTransactionId: '0x456',
from: 'mockFrom', from: 'mockFrom',
gas: 'mockGas', gas: 'mockGas',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedToken: { sendToken: {
address: 'mockTokenAddress', address: 'mockTokenAddress',
}, },
to: 'mockTo', to: 'mockTo',

View File

@ -34,7 +34,7 @@ export default class SendTransactionScreen extends Component {
primaryCurrency: PropTypes.string, primaryCurrency: PropTypes.string,
resetSendState: PropTypes.func.isRequired, resetSendState: PropTypes.func.isRequired,
selectedAddress: PropTypes.string, selectedAddress: PropTypes.string,
selectedToken: PropTypes.object, sendToken: PropTypes.object,
showHexData: PropTypes.bool, showHexData: PropTypes.bool,
to: PropTypes.string, to: PropTypes.string,
toNickname: PropTypes.string, toNickname: PropTypes.string,
@ -77,7 +77,7 @@ export default class SendTransactionScreen extends Component {
gasTotal, gasTotal,
network, network,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
updateSendErrors, updateSendErrors,
updateSendTo, updateSendTo,
@ -97,7 +97,7 @@ export default class SendTransactionScreen extends Component {
gasTotal: prevGasTotal, gasTotal: prevGasTotal,
tokenBalance: prevTokenBalance, tokenBalance: prevTokenBalance,
network: prevNetwork, network: prevNetwork,
selectedToken: prevSelectedToken, sendToken: prevSendToken,
to: prevTo, to: prevTo,
} = prevProps } = prevProps
@ -109,7 +109,7 @@ export default class SendTransactionScreen extends Component {
prevBalance, prevBalance,
prevGasTotal, prevGasTotal,
prevTokenBalance, prevTokenBalance,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
@ -120,16 +120,16 @@ export default class SendTransactionScreen extends Component {
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) })
const gasFeeErrorObject = selectedToken const gasFeeErrorObject = sendToken
? getGasFeeErrorObject({ ? getGasFeeErrorObject({
balance, balance,
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
}) })
: { gasFee: null } : { gasFee: null }
updateSendErrors(Object.assign(amountErrorObject, gasFeeErrorObject)) updateSendErrors(Object.assign(amountErrorObject, gasFeeErrorObject))
@ -139,7 +139,7 @@ export default class SendTransactionScreen extends Component {
if (network !== prevNetwork && network !== 'loading') { if (network !== prevNetwork && network !== 'loading') {
updateSendTokenBalance({ updateSendTokenBalance({
selectedToken, sendToken,
tokenContract, tokenContract,
address, address,
}) })
@ -148,10 +148,10 @@ export default class SendTransactionScreen extends Component {
} }
} }
const prevTokenAddress = prevSelectedToken && prevSelectedToken.address const prevTokenAddress = prevSendToken && prevSendToken.address
const selectedTokenAddress = selectedToken && selectedToken.address const sendTokenAddress = sendToken && sendToken.address
if (selectedTokenAddress && prevTokenAddress !== selectedTokenAddress) { if (sendTokenAddress && prevTokenAddress !== sendTokenAddress) {
this.updateSendToken() this.updateSendToken()
updateGas = true updateGas = true
} }
@ -220,7 +220,7 @@ export default class SendTransactionScreen extends Component {
const { const {
hasHexData, hasHexData,
tokens, tokens,
selectedToken, sendToken,
network, network,
} = this.props } = this.props
@ -228,8 +228,8 @@ export default class SendTransactionScreen extends Component {
return this.setState({ toError: '', toWarning: '' }) return this.setState({ toError: '', toWarning: '' })
} }
const toErrorObject = getToErrorObject(query, hasHexData, tokens, selectedToken, network) const toErrorObject = getToErrorObject(query, hasHexData, tokens, sendToken, network)
const toWarningObject = getToWarningObject(query, tokens, selectedToken) const toWarningObject = getToWarningObject(query, tokens, sendToken)
this.setState({ this.setState({
toError: toErrorObject.to, toError: toErrorObject.to,
@ -240,13 +240,13 @@ export default class SendTransactionScreen extends Component {
updateSendToken () { updateSendToken () {
const { const {
from: { address }, from: { address },
selectedToken, sendToken,
tokenContract, tokenContract,
updateSendTokenBalance, updateSendTokenBalance,
} = this.props } = this.props
updateSendTokenBalance({ updateSendTokenBalance({
selectedToken, sendToken,
tokenContract, tokenContract,
address, address,
}) })
@ -260,7 +260,7 @@ export default class SendTransactionScreen extends Component {
gasLimit, gasLimit,
gasPrice, gasPrice,
selectedAddress, selectedAddress,
selectedToken = {}, sendToken = {},
to: currentToAddress, to: currentToAddress,
updateAndSetGasLimit, updateAndSetGasLimit,
} = this.props } = this.props
@ -271,7 +271,7 @@ export default class SendTransactionScreen extends Component {
gasLimit, gasLimit,
gasPrice, gasPrice,
selectedAddress, selectedAddress,
selectedToken, sendToken,
to: getToAddressForGasUpdate(updatedToAddress, currentToAddress), to: getToAddressForGasUpdate(updatedToAddress, currentToAddress),
value: value || amount, value: value || amount,
data, data,

View File

@ -11,8 +11,8 @@ import {
getGasPrice, getGasPrice,
getGasTotal, getGasTotal,
getPrimaryCurrency, getPrimaryCurrency,
getSelectedToken, getSendToken,
getSelectedTokenContract, getSendTokenContract,
getSendAmount, getSendAmount,
getSendEditingTransactionId, getSendEditingTransactionId,
getSendHexDataFeatureFlagState, getSendHexDataFeatureFlagState,
@ -67,13 +67,13 @@ function mapStateToProps (state) {
primaryCurrency: getPrimaryCurrency(state), primaryCurrency: getPrimaryCurrency(state),
qrCodeData: getQrCodeData(state), qrCodeData: getQrCodeData(state),
selectedAddress: getSelectedAddress(state), selectedAddress: getSelectedAddress(state),
selectedToken: getSelectedToken(state), sendToken: getSendToken(state),
showHexData: getSendHexDataFeatureFlagState(state), showHexData: getSendHexDataFeatureFlagState(state),
to: getSendTo(state), to: getSendTo(state),
toNickname: getSendToNickname(state), toNickname: getSendToNickname(state),
tokens: getTokens(state), tokens: getTokens(state),
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
tokenContract: getSelectedTokenContract(state), tokenContract: getSendTokenContract(state),
} }
} }
@ -85,18 +85,18 @@ function mapDispatchToProps (dispatch) {
gasLimit, gasLimit,
gasPrice, gasPrice,
selectedAddress, selectedAddress,
selectedToken, sendToken,
to, to,
value, value,
data, data,
}) => { }) => {
!editingTransactionId !editingTransactionId
? dispatch(updateGasData({ gasPrice, selectedAddress, selectedToken, blockGasLimit, to, value, data })) ? dispatch(updateGasData({ gasPrice, selectedAddress, sendToken, blockGasLimit, to, value, data }))
: dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice))) : dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice)))
}, },
updateSendTokenBalance: ({ selectedToken, tokenContract, address }) => { updateSendTokenBalance: ({ sendToken, tokenContract, address }) => {
dispatch(updateSendTokenBalance({ dispatch(updateSendTokenBalance({
selectedToken, sendToken,
tokenContract, tokenContract,
address, address,
})) }))

View File

@ -105,11 +105,11 @@ function getAmountErrorObject ({
conversionRate, conversionRate,
gasTotal, gasTotal,
primaryCurrency, primaryCurrency,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) { }) {
let insufficientFunds = false let insufficientFunds = false
if (gasTotal && conversionRate && !selectedToken) { if (gasTotal && conversionRate && !sendToken) {
insufficientFunds = !isBalanceSufficient({ insufficientFunds = !isBalanceSufficient({
amount, amount,
balance, balance,
@ -120,8 +120,8 @@ function getAmountErrorObject ({
} }
let inSufficientTokens = false let inSufficientTokens = false
if (selectedToken && tokenBalance !== null) { if (sendToken && tokenBalance !== null) {
const { decimals } = selectedToken const { decimals } = sendToken
inSufficientTokens = !isTokenBalanceSufficient({ inSufficientTokens = !isTokenBalanceSufficient({
tokenBalance, tokenBalance,
amount, amount,
@ -172,8 +172,8 @@ function getGasFeeErrorObject ({
return { gasFee: gasFeeError } return { gasFee: gasFeeError }
} }
function calcTokenBalance ({ selectedToken, usersToken }) { function calcTokenBalance ({ sendToken, usersToken }) {
const { decimals } = selectedToken || {} const { decimals } = sendToken || {}
return calcTokenAmount(usersToken.balance.toString(), decimals).toString(16) return calcTokenAmount(usersToken.balance.toString(), decimals).toString(16)
} }
@ -183,12 +183,12 @@ function doesAmountErrorRequireUpdate ({
prevBalance, prevBalance,
prevGasTotal, prevGasTotal,
prevTokenBalance, prevTokenBalance,
selectedToken, sendToken,
tokenBalance, tokenBalance,
}) { }) {
const balanceHasChanged = balance !== prevBalance const balanceHasChanged = balance !== prevBalance
const gasTotalHasChange = gasTotal !== prevGasTotal const gasTotalHasChange = gasTotal !== prevGasTotal
const tokenBalanceHasChanged = selectedToken && tokenBalance !== prevTokenBalance const tokenBalanceHasChanged = sendToken && tokenBalance !== prevTokenBalance
const amountErrorRequiresUpdate = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged const amountErrorRequiresUpdate = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged
return amountErrorRequiresUpdate return amountErrorRequiresUpdate
@ -196,7 +196,7 @@ function doesAmountErrorRequireUpdate ({
async function estimateGas ({ async function estimateGas ({
selectedAddress, selectedAddress,
selectedToken, sendToken,
blockGasLimit = MIN_GAS_LIMIT_HEX, blockGasLimit = MIN_GAS_LIMIT_HEX,
to, to,
value, value,
@ -207,21 +207,21 @@ async function estimateGas ({
const paramsForGasEstimate = { from: selectedAddress, value, gasPrice } const paramsForGasEstimate = { from: selectedAddress, value, gasPrice }
// if recipient has no code, gas is 21k max: // if recipient has no code, gas is 21k max:
if (!selectedToken && !data) { if (!sendToken && !data) {
const code = Boolean(to) && await global.eth.getCode(to) const code = Boolean(to) && await global.eth.getCode(to)
// Geth will return '0x', and ganache-core v2.2.1 will return '0x0' // Geth will return '0x', and ganache-core v2.2.1 will return '0x0'
const codeIsEmpty = !code || code === '0x' || code === '0x0' const codeIsEmpty = !code || code === '0x' || code === '0x0'
if (codeIsEmpty) { if (codeIsEmpty) {
return SIMPLE_GAS_COST return SIMPLE_GAS_COST
} }
} else if (selectedToken && !to) { } else if (sendToken && !to) {
return BASE_TOKEN_GAS_COST return BASE_TOKEN_GAS_COST
} }
if (selectedToken) { if (sendToken) {
paramsForGasEstimate.value = '0x0' paramsForGasEstimate.value = '0x0'
paramsForGasEstimate.data = generateTokenTransferData({ toAddress: to, amount: value, selectedToken }) paramsForGasEstimate.data = generateTokenTransferData({ toAddress: to, amount: value, sendToken })
paramsForGasEstimate.to = selectedToken.address paramsForGasEstimate.to = sendToken.address
} else { } else {
if (data) { if (data) {
paramsForGasEstimate.data = data paramsForGasEstimate.data = data
@ -299,8 +299,8 @@ function addGasBuffer (initialGasLimitHex, blockGasLimitHex, bufferMultiplier =
return upperGasLimit return upperGasLimit
} }
function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', selectedToken }) { function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', sendToken }) {
if (!selectedToken) { if (!sendToken) {
return return
} }
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call( return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(

View File

@ -58,7 +58,7 @@ describe('Send Component', function () {
network="3" network="3"
primaryCurrency="mockPrimaryCurrency" primaryCurrency="mockPrimaryCurrency"
selectedAddress="mockSelectedAddress" selectedAddress="mockSelectedAddress"
selectedToken={{ address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }} sendToken={{ address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }}
showHexData showHexData
tokenBalance="mockTokenBalance" tokenBalance="mockTokenBalance"
tokenContract={{ method: 'mockTokenMethod' }} tokenContract={{ method: 'mockTokenMethod' }}
@ -141,7 +141,7 @@ describe('Send Component', function () {
prevBalance: '', prevBalance: '',
prevGasTotal: undefined, prevGasTotal: undefined,
prevTokenBalance: undefined, prevTokenBalance: undefined,
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' },
tokenBalance: 'mockTokenBalance', tokenBalance: 'mockTokenBalance',
} }
) )
@ -173,13 +173,13 @@ describe('Send Component', function () {
conversionRate: 10, conversionRate: 10,
gasTotal: 'mockGasTotal', gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency', primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' },
tokenBalance: 'mockTokenBalance', tokenBalance: 'mockTokenBalance',
} }
) )
}) })
it('should call getGasFeeErrorObject if doesAmountErrorRequireUpdate returns true and selectedToken is truthy', function () { it('should call getGasFeeErrorObject if doesAmountErrorRequireUpdate returns true and sendToken is truthy', function () {
utilsMethodStubs.getGasFeeErrorObject.resetHistory() utilsMethodStubs.getGasFeeErrorObject.resetHistory()
wrapper.instance().componentDidUpdate({ wrapper.instance().componentDidUpdate({
from: { from: {
@ -194,7 +194,7 @@ describe('Send Component', function () {
conversionRate: 10, conversionRate: 10,
gasTotal: 'mockGasTotal', gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency', primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' },
} }
) )
}) })
@ -207,9 +207,9 @@ describe('Send Component', function () {
assert.equal(utilsMethodStubs.getGasFeeErrorObject.callCount, 0) assert.equal(utilsMethodStubs.getGasFeeErrorObject.callCount, 0)
}) })
it('should not call getGasFeeErrorObject if doesAmountErrorRequireUpdate returns true but selectedToken is falsy', function () { it('should not call getGasFeeErrorObject if doesAmountErrorRequireUpdate returns true but sendToken is falsy', function () {
utilsMethodStubs.getGasFeeErrorObject.resetHistory() utilsMethodStubs.getGasFeeErrorObject.resetHistory()
wrapper.setProps({ selectedToken: null }) wrapper.setProps({ sendToken: null })
wrapper.instance().componentDidUpdate({ wrapper.instance().componentDidUpdate({
from: { from: {
balance: 'balanceChanged', balance: 'balanceChanged',
@ -218,9 +218,9 @@ describe('Send Component', function () {
assert.equal(utilsMethodStubs.getGasFeeErrorObject.callCount, 0) assert.equal(utilsMethodStubs.getGasFeeErrorObject.callCount, 0)
}) })
it('should call updateSendErrors with the expected params if selectedToken is falsy', function () { it('should call updateSendErrors with the expected params if sendToken is falsy', function () {
propsMethodSpies.updateSendErrors.resetHistory() propsMethodSpies.updateSendErrors.resetHistory()
wrapper.setProps({ selectedToken: null }) wrapper.setProps({ sendToken: null })
wrapper.instance().componentDidUpdate({ wrapper.instance().componentDidUpdate({
from: { from: {
balance: 'balanceChanged', balance: 'balanceChanged',
@ -233,9 +233,9 @@ describe('Send Component', function () {
) )
}) })
it('should call updateSendErrors with the expected params if selectedToken is truthy', function () { it('should call updateSendErrors with the expected params if sendToken is truthy', function () {
propsMethodSpies.updateSendErrors.resetHistory() propsMethodSpies.updateSendErrors.resetHistory()
wrapper.setProps({ selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' } }) wrapper.setProps({ sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' } })
wrapper.instance().componentDidUpdate({ wrapper.instance().componentDidUpdate({
from: { from: {
balance: 'balanceChanged', balance: 'balanceChanged',
@ -256,7 +256,7 @@ describe('Send Component', function () {
balance: 'balanceChanged', balance: 'balanceChanged',
}, },
network: '3', network: '3',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset
}) })
assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0) assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0)
assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0)
@ -271,7 +271,7 @@ describe('Send Component', function () {
balance: 'balanceChanged', balance: 'balanceChanged',
}, },
network: '3', network: '3',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset
}) })
assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0) assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0)
assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0)
@ -285,13 +285,13 @@ describe('Send Component', function () {
balance: 'balanceChanged', balance: 'balanceChanged',
}, },
network: '2', network: '2',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset
}) })
assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 1) assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 1)
assert.deepEqual( assert.deepEqual(
propsMethodSpies.updateSendTokenBalance.getCall(0).args[0], propsMethodSpies.updateSendTokenBalance.getCall(0).args[0],
{ {
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, // Make sure not to hit updateGas when changing asset
tokenContract: { method: 'mockTokenMethod' }, tokenContract: { method: 'mockTokenMethod' },
address: 'mockAddress', address: 'mockAddress',
} }
@ -303,7 +303,7 @@ describe('Send Component', function () {
) )
}) })
it('should call updateGas when selectedToken.address is changed', function () { it('should call updateGas when sendToken.address is changed', function () {
SendTransactionScreen.prototype.updateGas.resetHistory() SendTransactionScreen.prototype.updateGas.resetHistory()
propsMethodSpies.updateAndSetGasLimit.resetHistory() propsMethodSpies.updateAndSetGasLimit.resetHistory()
wrapper.instance().componentDidUpdate({ wrapper.instance().componentDidUpdate({
@ -311,7 +311,7 @@ describe('Send Component', function () {
balance: 'balancedChanged', balance: 'balancedChanged',
}, },
network: '3', // Make sure not to hit updateGas when changing network network: '3', // Make sure not to hit updateGas when changing network
selectedToken: { address: 'newSelectedToken' }, sendToken: { address: 'newSelectedToken' },
}) })
assert.equal(propsMethodSpies.updateToNicknameIfNecessary.callCount, 0) // Network did not change assert.equal(propsMethodSpies.updateToNicknameIfNecessary.callCount, 0) // Network did not change
assert.equal(propsMethodSpies.updateAndSetGasLimit.callCount, 1) assert.equal(propsMethodSpies.updateAndSetGasLimit.callCount, 1)
@ -331,7 +331,7 @@ describe('Send Component', function () {
gasLimit: 'mockGasLimit', gasLimit: 'mockGasLimit',
gasPrice: 'mockGasPrice', gasPrice: 'mockGasPrice',
selectedAddress: 'mockSelectedAddress', selectedAddress: 'mockSelectedAddress',
selectedToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' }, sendToken: { address: 'mockTokenAddress', decimals: 18, symbol: 'TST' },
to: '', to: '',
value: 'mockAmount', value: 'mockAmount',
data: undefined, data: undefined,

View File

@ -49,7 +49,7 @@ describe('send container', function () {
gasLimit: '0x3', gasLimit: '0x3',
gasPrice: '0x4', gasPrice: '0x4',
selectedAddress: '0x4', selectedAddress: '0x4',
selectedToken: { address: '0x1' }, sendToken: { address: '0x1' },
to: 'mockTo', to: 'mockTo',
value: 'mockValue', value: 'mockValue',
data: undefined, data: undefined,
@ -65,14 +65,14 @@ describe('send container', function () {
}) })
it('should dispatch an updateGasData action when editingTransactionId is falsy', function () { it('should dispatch an updateGasData action when editingTransactionId is falsy', function () {
const { gasPrice, selectedAddress, selectedToken, blockGasLimit, to, value, data } = mockProps const { gasPrice, selectedAddress, sendToken, blockGasLimit, to, value, data } = mockProps
mapDispatchToPropsObject.updateAndSetGasLimit( mapDispatchToPropsObject.updateAndSetGasLimit(
Object.assign({}, mockProps, { editingTransactionId: false }) Object.assign({}, mockProps, { editingTransactionId: false })
) )
assert(dispatchSpy.calledOnce) assert(dispatchSpy.calledOnce)
assert.deepEqual( assert.deepEqual(
actionSpies.updateGasData.getCall(0).args[0], actionSpies.updateGasData.getCall(0).args[0],
{ gasPrice, selectedAddress, selectedToken, blockGasLimit, to, value, data } { gasPrice, selectedAddress, sendToken, blockGasLimit, to, value, data }
) )
}) })
}) })
@ -81,7 +81,7 @@ describe('send container', function () {
const mockProps = { const mockProps = {
address: '0x10', address: '0x10',
tokenContract: '0x00a', tokenContract: '0x00a',
selectedToken: { address: '0x1' }, sendToken: { address: '0x1' },
} }
it('should dispatch an action', function () { it('should dispatch an action', function () {

View File

@ -89,7 +89,7 @@ describe('send utils', function () {
'should return true if token balances are different': { 'should return true if token balances are different': {
tokenBalance: 0, tokenBalance: 0,
prevTokenBalance: 1, prevTokenBalance: 1,
selectedToken: 'someToken', sendToken: { address: '0x0' },
expectedResult: true, expectedResult: true,
}, },
'should return false if they are all the same': { 'should return false if they are all the same': {
@ -99,7 +99,7 @@ describe('send utils', function () {
prevGasTotal: 1, prevGasTotal: 1,
tokenBalance: 1, tokenBalance: 1,
prevTokenBalance: 1, prevTokenBalance: 1,
selectedToken: 'someToken', sendToken: { address: '0x0' },
expectedResult: false, expectedResult: false,
}, },
} }
@ -112,13 +112,13 @@ describe('send utils', function () {
}) })
describe('generateTokenTransferData()', function () { describe('generateTokenTransferData()', function () {
it('should return undefined if not passed a selected token', function () { it('should return undefined if not passed a send token', function () {
assert.equal(generateTokenTransferData({ toAddress: 'mockAddress', amount: '0xa', selectedToken: false }), undefined) assert.equal(generateTokenTransferData({ toAddress: 'mockAddress', amount: '0xa', sendToken: undefined }), undefined)
}) })
it('should call abi.rawEncode with the correct params', function () { it('should call abi.rawEncode with the correct params', function () {
stubs.rawEncode.resetHistory() stubs.rawEncode.resetHistory()
generateTokenTransferData({ toAddress: 'mockAddress', amount: 'ab', selectedToken: true }) generateTokenTransferData({ toAddress: 'mockAddress', amount: 'ab', sendToken: { address: '0x0' } })
assert.deepEqual( assert.deepEqual(
stubs.rawEncode.getCall(0).args, stubs.rawEncode.getCall(0).args,
[['address', 'uint256'], ['mockAddress', '0xab']] [['address', 'uint256'], ['mockAddress', '0xab']]
@ -127,7 +127,7 @@ describe('send utils', function () {
it('should return encoded token transfer data', function () { it('should return encoded token transfer data', function () {
assert.equal( assert.equal(
generateTokenTransferData({ toAddress: 'mockAddress', amount: '0xa', selectedToken: true }), generateTokenTransferData({ toAddress: 'mockAddress', amount: '0xa', sendToken: { address: '0x0' } }),
'0xa9059cbb104c' '0xa9059cbb104c'
) )
}) })
@ -143,13 +143,13 @@ describe('send utils', function () {
primaryCurrency: 'ABC', primaryCurrency: 'ABC',
expectedResult: { amount: INSUFFICIENT_FUNDS_ERROR }, expectedResult: { amount: INSUFFICIENT_FUNDS_ERROR },
}, },
'should not return insufficientFunds error if selectedToken is truthy': { 'should not return insufficientFunds error if sendToken is truthy': {
amount: '0x0', amount: '0x0',
balance: 1, balance: 1,
conversionRate: 3, conversionRate: 3,
gasTotal: 17, gasTotal: 17,
primaryCurrency: 'ABC', primaryCurrency: 'ABC',
selectedToken: { symbole: 'DEF', decimals: 0 }, sendToken: { address: '0x0', symbol: 'DEF', decimals: 0 },
decimals: 0, decimals: 0,
tokenBalance: 'sometokenbalance', tokenBalance: 'sometokenbalance',
expectedResult: { amount: null }, expectedResult: { amount: null },
@ -161,7 +161,7 @@ describe('send utils', function () {
decimals: 10, decimals: 10,
gasTotal: 17, gasTotal: 17,
primaryCurrency: 'ABC', primaryCurrency: 'ABC',
selectedToken: 'someToken', sendToken: { address: '0x0' },
tokenBalance: 123, tokenBalance: 123,
expectedResult: { amount: INSUFFICIENT_TOKENS_ERROR }, expectedResult: { amount: INSUFFICIENT_TOKENS_ERROR },
}, },
@ -200,7 +200,8 @@ describe('send utils', function () {
describe('calcTokenBalance()', function () { describe('calcTokenBalance()', function () {
it('should return the calculated token blance', function () { it('should return the calculated token blance', function () {
assert.equal(calcTokenBalance({ assert.equal(calcTokenBalance({
selectedToken: { sendToken: {
address: '0x0',
decimals: 11, decimals: 11,
}, },
usersToken: { usersToken: {
@ -340,8 +341,8 @@ describe('send utils', function () {
assert.equal(result, '0xabc16x1.5') assert.equal(result, '0xabc16x1.5')
}) })
it('should call ethQuery.estimateGas with a value of 0x0 and the expected data and to if passed a selectedToken', async function () { it('should call ethQuery.estimateGas with a value of 0x0 and the expected data and to if passed a sendToken', async function () {
const result = await estimateGas(Object.assign({ data: 'mockData', selectedToken: { address: 'mockAddress' } }, baseMockParams)) const result = await estimateGas(Object.assign({ data: 'mockData', sendToken: { address: 'mockAddress' } }, baseMockParams))
assert.equal(baseMockParams.estimateGasMethod.callCount, 1) assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
assert.deepEqual( assert.deepEqual(
baseMockParams.estimateGasMethod.getCall(0).args[0], baseMockParams.estimateGasMethod.getCall(0).args[0],
@ -373,20 +374,20 @@ describe('send utils', function () {
assert.equal(result, SIMPLE_GAS_COST) assert.equal(result, SIMPLE_GAS_COST)
}) })
it(`should return ${SIMPLE_GAS_COST} if not passed a selectedToken or truthy to address`, async function () { it(`should return ${SIMPLE_GAS_COST} if not passed a sendToken or truthy to address`, async function () {
assert.equal(baseMockParams.estimateGasMethod.callCount, 0) assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
const result = await estimateGas(Object.assign({}, baseMockParams, { to: null })) const result = await estimateGas(Object.assign({}, baseMockParams, { to: null }))
assert.equal(result, SIMPLE_GAS_COST) assert.equal(result, SIMPLE_GAS_COST)
}) })
it(`should not return ${SIMPLE_GAS_COST} if passed a selectedToken`, async function () { it(`should not return ${SIMPLE_GAS_COST} if passed a sendToken`, async function () {
assert.equal(baseMockParams.estimateGasMethod.callCount, 0) assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
const result = await estimateGas(Object.assign({}, baseMockParams, { to: '0x123', selectedToken: { address: '' } })) const result = await estimateGas(Object.assign({}, baseMockParams, { to: '0x123', sendToken: { address: '0x0' } }))
assert.notEqual(result, SIMPLE_GAS_COST) assert.notEqual(result, SIMPLE_GAS_COST)
}) })
it(`should return ${BASE_TOKEN_GAS_COST} if passed a selectedToken but no to address`, async function () { it(`should return ${BASE_TOKEN_GAS_COST} if passed a sendToken but no to address`, async function () {
const result = await estimateGas(Object.assign({}, baseMockParams, { to: null, selectedToken: { address: '' } })) const result = await estimateGas(Object.assign({}, baseMockParams, { to: null, sendToken: { address: '0x0' } }))
assert.equal(result, BASE_TOKEN_GAS_COST) assert.equal(result, BASE_TOKEN_GAS_COST)
}) })

View File

@ -6,9 +6,18 @@ import {
checksumAddress, checksumAddress,
getAccountByAddress, getAccountByAddress,
} from '../helpers/utils/util' } from '../helpers/utils/util'
import { import { getSendToken } from './send'
getSelectedToken, import { getTokens } from '../ducks/metamask/metamask'
} from '.'
export const getSelectedTokenAddress = (state) => state.metamask.selectedTokenAddress
export const getSelectedToken = createSelector(
getTokens,
getSelectedTokenAddress,
(tokens, selectedTokenAddress) => {
return tokens.find(({ address }) => address === selectedTokenAddress)
}
)
export function getNetworkIdentifier (state) { export function getNetworkIdentifier (state) {
const { metamask: { provider: { type, nickname, rpcTarget } } } = state const { metamask: { provider: { type, nickname, rpcTarget } } } = state
@ -48,10 +57,14 @@ export function getAccountType (state) {
} }
} }
export function getSelectedAsset (state) { export const getSelectedAsset = createSelector(
const selectedToken = getSelectedToken(state) getSelectedToken,
return (selectedToken && selectedToken.symbol) || 'ETH' getSendToken,
} (selectedToken, sendToken) => {
const token = selectedToken || sendToken
return token?.symbol || 'ETH'
}
)
export function getCurrentNetworkId (state) { export function getCurrentNetworkId (state) {
return state.metamask.network return state.metamask.network
@ -314,7 +327,7 @@ export function getTargetDomainMetadata (state, request, defaultOrigin) {
return targetDomainMetadata return targetDomainMetadata
} }
export function getMetaMetricState (state) { export const getMetaMetricState = (state) => {
return { return {
network: getCurrentNetworkId(state), network: getCurrentNetworkId(state),
activeCurrency: getSelectedAsset(state), activeCurrency: getSelectedAsset(state),

View File

@ -37,23 +37,22 @@ export function getGasTotal (state) {
} }
export function getPrimaryCurrency (state) { export function getPrimaryCurrency (state) {
const selectedToken = getSelectedToken(state) const sendToken = getSendToken(state)
return selectedToken && selectedToken.symbol return sendToken?.symbol
} }
export function getSelectedToken (state) { export function getSendToken (state) {
const tokens = state.metamask.tokens || [] return state.metamask.send.token
const selectedTokenAddress = state.metamask.selectedTokenAddress
const selectedToken = tokens.filter(({ address }) => address === selectedTokenAddress)[0]
const sendToken = state.metamask?.send.token
return selectedToken || sendToken || null
} }
export function getSelectedTokenContract (state) { export function getSendTokenAddress (state) {
const selectedToken = getSelectedToken(state) return getSendToken(state)?.address
return selectedToken }
? global.eth.contract(abi).at(selectedToken.address)
export function getSendTokenContract (state) {
const sendTokenAddress = getSendTokenAddress(state)
return sendTokenAddress
? global.eth.contract(abi).at(sendTokenAddress)
: null : null
} }
@ -148,7 +147,7 @@ export function getGasButtonGroupShown (state) {
export function getTitleKey (state) { export function getTitleKey (state) {
const isEditing = Boolean(getSendEditingTransactionId(state)) const isEditing = Boolean(getSendEditingTransactionId(state))
const isToken = Boolean(getSelectedToken(state)) const isToken = Boolean(getSendToken(state))
if (!getSendTo(state)) { if (!getSendTo(state)) {
return 'addRecipient' return 'addRecipient'

View File

@ -13,8 +13,8 @@ import {
getGasPrice, getGasPrice,
getGasTotal, getGasTotal,
getPrimaryCurrency, getPrimaryCurrency,
getSelectedToken, getSendToken,
getSelectedTokenContract, getSendTokenContract,
getSendAmount, getSendAmount,
sendAmountIsInError, sendAmountIsInError,
getSendEditingTransactionId, getSendEditingTransactionId,
@ -167,18 +167,28 @@ describe('send selectors', function () {
}) })
describe('getPrimaryCurrency()', function () { describe('getPrimaryCurrency()', function () {
it('should return the symbol of the selected token', function () { it('should return the symbol of the send token', function () {
assert.equal( assert.equal(
getPrimaryCurrency(mockState), getPrimaryCurrency({ metamask: { send: { token: { symbol: 'DEF' } } } }),
'DEF' 'DEF'
) )
}) })
}) })
describe('getSelectedToken()', function () { describe('getSendToken()', function () {
it('should return the currently selected token if selected', function () { it('should return the current send token if set', function () {
assert.deepEqual( assert.deepEqual(
getSelectedToken(mockState), getSendToken({
metamask: {
send: {
token: {
address: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
decimals: 4,
symbol: 'DEF',
},
},
},
}),
{ {
address: '0x8d6b81208414189a58339873ab429b6c47ab92d3', address: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
decimals: 4, decimals: 4,
@ -186,40 +196,30 @@ describe('send selectors', function () {
} }
) )
}) })
it('should return the send token if none is currently selected, but a send token exists', function () {
const mockSendToken = {
address: '0x123456708414189a58339873ab429b6c47ab92d3',
decimals: 4,
symbol: 'JKL',
}
const editedMockState = {
metamask: Object.assign({}, mockState.metamask, {
selectedTokenAddress: null,
send: {
token: mockSendToken,
},
}),
}
assert.deepEqual(
getSelectedToken(editedMockState),
Object.assign({}, mockSendToken)
)
})
}) })
describe('getSelectedTokenContract()', function () { describe('getSendTokenContract()', function () {
it('should return the contract at the selected token address', function () { it('should return the contract at the send token address', function () {
assert.equal( assert.equal(
getSelectedTokenContract(mockState), getSendTokenContract({
metamask: {
send: {
token: {
address: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
decimals: 4,
symbol: 'DEF',
},
},
},
}),
'mockAt:0x8d6b81208414189a58339873ab429b6c47ab92d3' 'mockAt:0x8d6b81208414189a58339873ab429b6c47ab92d3'
) )
}) })
it('should return null if no token is selected', function () { it('should return null if send token is not set', function () {
const modifiedMetamaskState = Object.assign({}, mockState.metamask, { selectedTokenAddress: false }) const modifiedMetamaskState = Object.assign({}, mockState.metamask, { send: {} })
assert.equal( assert.equal(
getSelectedTokenContract(Object.assign({}, mockState, { metamask: modifiedMetamaskState })), getSendTokenContract(Object.assign({}, mockState, { metamask: modifiedMetamaskState })),
null null
) )
}) })
@ -530,29 +530,29 @@ describe('send selectors', function () {
getMetamaskSendMockState({ getMetamaskSendMockState({
to: true, to: true,
editingTransactionId: true, editingTransactionId: true,
token: true, // this can be whatever token: {},
}) })
), 'edit') ), 'edit')
}) })
it('should return the correct key when getSendEditingTransactionId is falsy and getSelectedToken is truthy', function () { it('should return the correct key when getSendEditingTransactionId is falsy and getSendToken is truthy', function () {
assert.equal( assert.equal(
getTitleKey( getTitleKey(
getMetamaskSendMockState({ getMetamaskSendMockState({
to: true, to: true,
editingTransactionId: false, editingTransactionId: false,
token: true, token: {},
}) })
), 'sendTokens') ), 'sendTokens')
}) })
it('should return the correct key when getSendEditingTransactionId is falsy and getSelectedToken is falsy', function () { it('should return the correct key when getSendEditingTransactionId is falsy and getSendToken is falsy', function () {
assert.equal( assert.equal(
getTitleKey( getTitleKey(
getMetamaskSendMockState({ getMetamaskSendMockState({
to: true, to: true,
editingTransactionId: false, editingTransactionId: false,
token: false, token: null,
}) })
), 'sendETH') ), 'sendETH')
}) })

View File

@ -607,7 +607,7 @@ export function updateGasData ({
gasPrice, gasPrice,
blockGasLimit, blockGasLimit,
selectedAddress, selectedAddress,
selectedToken, sendToken,
to, to,
value, value,
data, data,
@ -618,7 +618,7 @@ export function updateGasData ({
estimateGasMethod: promisifiedBackground.estimateGas, estimateGasMethod: promisifiedBackground.estimateGas,
blockGasLimit, blockGasLimit,
selectedAddress, selectedAddress,
selectedToken, sendToken,
to, to,
value, value,
estimateGasPrice: gasPrice, estimateGasPrice: gasPrice,
@ -651,7 +651,7 @@ export function gasLoadingFinished () {
} }
export function updateSendTokenBalance ({ export function updateSendTokenBalance ({
selectedToken, sendToken,
tokenContract, tokenContract,
address, address,
}) { }) {
@ -662,7 +662,7 @@ export function updateSendTokenBalance ({
return tokenBalancePromise return tokenBalancePromise
.then((usersToken) => { .then((usersToken) => {
if (usersToken) { if (usersToken) {
const newTokenBalance = calcTokenBalance({ selectedToken, usersToken }) const newTokenBalance = calcTokenBalance({ sendToken, usersToken })
dispatch(setSendTokenBalance(newTokenBalance)) dispatch(setSendTokenBalance(newTokenBalance))
} }
}) })