1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 19:26:13 +02:00

Further refactors; includes refactor of send-v2.js and associated container

This commit is contained in:
Dan 2018-04-27 16:33:00 -02:30
parent 33c16d1bf6
commit 26f965bcce
25 changed files with 489 additions and 156 deletions

View File

@ -2,7 +2,8 @@ const fs = require('fs')
const path = require('path')
const pump = require('pump')
const browserify = require('browserify')
const tests = fs.readdirSync(path.join(__dirname, 'lib'))
let tests = fs.readdirSync(path.join(__dirname, 'lib'))
tests = tests.filter(fln => fln.match(/send/))
const bundlePath = path.join(__dirname, 'bundle.js')
const b = browserify()

View File

@ -1,6 +1,11 @@
const abi = require('human-standard-token-abi')
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
const { getTokenAddressFromTokenObject } = require('./util')
const {
calcGasTotal,
getParamsForGasEstimate,
calcTokenBalance,
} = require('./components/send_/send.utils')
const ethUtil = require('ethereumjs-util')
const { fetchLocale } = require('../i18n-helper')
const log = require('loglevel')
@ -173,14 +178,16 @@ var actions = {
updateGasLimit,
updateGasPrice,
updateGasTotal,
setGasTotal,
setSendTokenBalance,
updateSendTokenBalance,
updateSendFrom,
updateSendTo,
updateSendAmount,
updateSendMemo,
updateSendErrors,
setMaxModeTo,
updateSend,
updateSendErrors,
clearSend,
setSelectedAddress,
// app messages
@ -716,14 +723,64 @@ function updateGasPrice (gasPrice) {
}
}
function updateGasTotal (gasTotal) {
function setGasTotal (gasTotal) {
return {
type: actions.UPDATE_GAS_TOTAL,
value: gasTotal,
}
}
function updateSendTokenBalance (tokenBalance) {
function updateGasTotal ({ selectedAddress, selectedToken, data }) {
return (dispatch) => {
const { symbol } = selectedToken || {}
const estimateGasParams = getParamsForGasEstimate(selectedAddress, symbol, data)
return Promise.all([
dispatch(actions.getGasPrice()),
dispatch(actions.estimateGas(estimateGasParams)),
])
.then(([gasPrice, gas]) => {
const newGasTotal = calcGasTotal(gas, gasPrice)
dispatch(actions.setGasTotal(newGasTotal))
dispatch(updateSendErrors({ gasLoadingError: null }))
})
.catch(err => {
log.error(err)
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' }))
})
}
}
function updateSendTokenBalance ({
selectedToken,
tokenContract,
address,
}) {
return (dispatch) => {
const tokenBalancePromise = tokenContract
? tokenContract.balanceOf(address)
: Promise.resolve()
return tokenBalancePromise
.then(usersToken => {
if (usersToken) {
const newTokenBalance = calcTokenBalance({ selectedToken, usersToken })
dispatch(setSendTokenBalance(newTokenBalance))
}
})
.catch(err => {
log.error(err)
updateSendErrors({ tokenBalance: 'tokenBalanceError' })
})
}
}
function updateSendErrors (errorObject) {
return {
type: actions.UPDATE_SEND_ERRORS,
value: errorObject,
}
}
function setSendTokenBalance (tokenBalance) {
return {
type: actions.UPDATE_SEND_TOKEN_BALANCE,
value: tokenBalance,
@ -758,13 +815,6 @@ function updateSendMemo (memo) {
}
}
function updateSendErrors (error) {
return {
type: actions.UPDATE_SEND_ERRORS,
value: error,
}
}
function setMaxModeTo (bool) {
return {
type: actions.UPDATE_MAX_MODE,

View File

@ -11,7 +11,7 @@ const log = require('loglevel')
// init
const InitializeScreen = require('../../mascara/src/app/first-time').default
// accounts
const SendTransactionScreen2 = require('./components/send/send-v2-container')
const SendTransactionScreen = require('./components/send_/send.container')
const ConfirmTxScreen = require('./conf-tx')
// slideout menu
@ -84,7 +84,7 @@ class App extends Component {
h(Initialized, { path: RESTORE_VAULT_ROUTE, exact, component: RestoreVaultPage }),
h(Initialized, { path: NOTICE_ROUTE, exact, component: NoticeScreen }),
h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, component: ConfirmTxScreen }),
h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }),
h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen }),
h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }),
h(Authenticated, { path: NEW_ACCOUNT_ROUTE, component: CreateAccountPage }),
h(Authenticated, { path: DEFAULT_ROUTE, exact, component: Home }),

View File

@ -8,6 +8,10 @@ const GasModalCard = require('./gas-modal-card')
const ethUtil = require('ethereumjs-util')
import {
updateSendErrors,
} from '../../ducks/send'
const {
MIN_GAS_PRICE_DEC,
MIN_GAS_LIMIT_DEC,
@ -63,9 +67,9 @@ function mapDispatchToProps (dispatch) {
hideModal: () => dispatch(actions.hideModal()),
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
updateGasTotal: newGasTotal => dispatch(actions.updateGasTotal(newGasTotal)),
updateGasTotal: newGasTotal => dispatch(actions.setGasTotal(newGasTotal)),
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendErrors: error => dispatch(actions.updateSendErrors(error)),
updateSendErrors: error => dispatch(updateSendErrors(error)),
}
}

View File

@ -29,6 +29,10 @@ const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
import {
updateSendErrors,
} from '../../ducks/send'
ConfirmSendEther.contextTypes = {
t: PropTypes.func,
}
@ -105,7 +109,7 @@ function mapDispatchToProps (dispatch) {
}))
dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
},
updateSendErrors: error => dispatch(actions.updateSendErrors(error)),
updateSendErrors: error => dispatch(updateSendErrors(error)),
}
}

View File

@ -39,6 +39,10 @@ const {
} = require('../../selectors')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
import {
updateSendErrors,
} from '../../ducks/send'
ConfirmSendToken.contextTypes = {
t: PropTypes.func,
}
@ -141,7 +145,7 @@ function mapDispatchToProps (dispatch, ownProps) {
}))
dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
},
updateSendErrors: error => dispatch(actions.updateSendErrors(error)),
updateSendErrors: error => dispatch(updateSendErrors(error)),
}
}

View File

@ -18,6 +18,10 @@ const {
getSelectedTokenContract,
} = require('../../selectors')
import {
updateSendErrors,
} from '../../ducks/send'
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
@ -74,7 +78,8 @@ function mapDispatchToProps (dispatch) {
updateTx: txData => dispatch(actions.updateTransaction(txData)),
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
addToAddressBook: (address, nickname) => dispatch(actions.addToAddressBook(address, nickname)),
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
setGasTotal: newTotal => dispatch(actions.setGasTotal(newTotal)),
updateGasTotal: () => dispatch(actions.updateGasTotal()),
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)),
@ -82,7 +87,7 @@ function mapDispatchToProps (dispatch) {
updateSendTo: (newTo, nickname) => dispatch(actions.updateSendTo(newTo, nickname)),
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
updateSendErrors: newError => dispatch(updateSendErrors(newError)),
clearSend: () => dispatch(actions.clearSend()),
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
}

View File

@ -12,4 +12,4 @@ function mapStateToProps (state) {
conversionRate: getConversionRate(state),
currentCurrency: getConvertedCurrency(state),
}
}
}

View File

@ -9,10 +9,12 @@ import { getMaxModeOn } from '../send-amount-row.selectors.js'
import { calcMaxAmount } from './amount-max-button.utils.js'
import {
updateSendAmount,
updateSendErrors,
setMaxModeTo,
} from '../../../actions'
} from '../../../../../actions'
import AmountMaxButton from './amount-max-button.component'
import {
updateSendErrors,
} from '../../../../../ducks/send'
export default connect(mapStateToProps, mapDispatchToProps)(AmountMaxButton)

View File

@ -8,7 +8,10 @@ export default class SendAmountRow extends Component {
static propTypes = {
amount: PropTypes.string,
amountConversionRate: PropTypes.number,
amountConversionRate: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
balance: PropTypes.string,
conversionRate: PropTypes.number,
convertedCurrency: PropTypes.string,

View File

@ -1,24 +1,26 @@
import { connect } from 'react-redux'
import {
getAmountConversionRate,
getConversionRate,
getConvertedCurrency,
getGasTotal,
getPrimaryCurrency,
getSelectedToken,
getSendAmount,
getSendFromBalance,
getTokenBalance,
} from '../../send.selectors.js'
} from '../../send.selectors'
import {
getAmountConversionRate,
getPrimaryCurrency,
sendAmountIsInError,
} from './send-amount-row.selectors.js'
import { getAmountErrorObject } from './send-amount-row.utils.js'
} from './send-amount-row.selectors'
import { getAmountErrorObject } from '../../send.utils'
import {
setMaxModeTo,
updateSendAmount,
updateSendErrors,
} from '../../../../actions'
import {
updateSendErrors,
} from '../../../../ducks/send'
import SendAmountRow from './send-amount-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendAmountRow)

View File

@ -1,13 +1,5 @@
import {
getSelectedToken,
getSelectedTokenToFiatRate,
getConversionRate,
} from '../../send.selectors.js'
const selectors = {
getAmountConversionRate,
getMaxModeOn,
getPrimaryCurrency,
sendAmountIsInError,
}
@ -18,16 +10,5 @@ function getMaxModeOn (state) {
}
function sendAmountIsInError (state) {
return Boolean(state.metamask.send.errors.amount)
}
function getPrimaryCurrency (state) {
const selectedToken = getSelectedToken(state)
return selectedToken && selectedToken.symbol
}
function getAmountConversionRate (state) {
return getSelectedToken(state)
? getSelectedTokenToFiatRate(state)
: getConversionRate(state)
return Boolean(state.send.errors.amount)
}

View File

@ -1,61 +0,0 @@
const {
conversionGreaterThan,
} = require('../../../../conversion-util')
const {
isBalanceSufficient,
isTokenBalanceSufficient,
} = require('../../send.utils')
function getAmountErrorObject ({
amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
}) {
let insufficientFunds = false
if (gasTotal && conversionRate) {
insufficientFunds = !isBalanceSufficient({
amount: selectedToken ? '0x0' : amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
})
}
let inSufficientTokens = false
if (selectedToken && tokenBalance !== null) {
const { decimals } = selectedToken
inSufficientTokens = !isTokenBalanceSufficient({
tokenBalance,
amount,
decimals,
})
}
const amountLessThanZero = conversionGreaterThan(
{ value: 0, fromNumericBase: 'dec' },
{ value: amount, fromNumericBase: 'hex' },
)
let amountError = null
if (insufficientFunds) {
amountError = 'insufficientFunds'
} else if (inSufficientTokens) {
amountError = 'insufficientTokens'
} else if (amountLessThanZero) {
amountError = 'negativeETH'
}
return { amount: amountError }
}
module.exports = {
getAmountErrorObject,
}

View File

@ -14,19 +14,19 @@ export default class SendFromRow extends Component {
openFromDropdown: PropTypes.func,
tokenContract: PropTypes.object,
updateSendFrom: PropTypes.func,
updateSendTokenBalance: PropTypes.func,
setSendTokenBalance: PropTypes.func,
};
async handleFromChange (newFrom) {
const {
updateSendFrom,
tokenContract,
updateSendTokenBalance,
setSendTokenBalance,
} = this.props
if (tokenContract) {
const usersToken = await tokenContract.balanceOf(newFrom.address)
updateSendTokenBalance(usersToken)
setSendTokenBalance(usersToken)
}
updateSendFrom(newFrom)
}

View File

@ -11,7 +11,7 @@ import {
import { calcTokenUpdateAmount } from './send-from-row.utils.js'
import {
updateSendFrom,
updateSendTokenBalance,
setSendTokenBalance,
} from '../../../../actions'
import {
closeFromDropdown,
@ -36,11 +36,11 @@ function mapDispatchToProps (dispatch) {
closeFromDropdown: () => dispatch(closeFromDropdown()),
openFromDropdown: () => dispatch(openFromDropdown()),
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)),
updateSendTokenBalance: (usersToken, selectedToken) => {
setSendTokenBalance: (usersToken, selectedToken) => {
if (!usersToken) return
const tokenBalance = calcTokenUpdateAmount(selectedToken, selectedToken)
dispatch(updateSendTokenBalance(tokenBalance))
dispatch(setSendTokenBalance(tokenBalance))
},
}
}

View File

@ -18,23 +18,8 @@ export default class SendGasRow extends Component {
showCustomizeGasModal: PropTypes.func,
tokenContract: PropTypes.object,
updateSendFrom: PropTypes.func,
updateSendTokenBalance: PropTypes.func,
};
async handleFromChange (newFrom) {
const {
tokenContract,
updateSendFrom,
updateSendTokenBalance,
} = this.props
if (tokenContract) {
const usersToken = await tokenContract.balanceOf(newFrom.address)
updateSendTokenBalance(usersToken)
}
updateSendFrom(newFrom)
}
render () {
const {
conversionRate,

View File

@ -10,6 +10,7 @@ export default class SendRowErrorMessage extends Component {
render () {
const { errors, errorType } = this.props
const errorMessage = errors[errorType]
return (

View File

@ -10,12 +10,12 @@ import {
} from './send-to-row.selectors.js'
import { getToErrorObject } from './send-to-row.utils.js'
import {
updateSendErrors,
updateSendTo,
} from '../../../../actions'
import {
openToDropdown,
closeToDropdown,
updateSendErrors,
openToDropdown,
closeToDropdown,
} from '../../../../ducks/send'
import SendToRow from './send-to-row.component'

View File

@ -0,0 +1,143 @@
import React from 'react'
import PropTypes from 'prop-types'
import PersistentForm from '../../../lib/persistent-form'
import {
getAmountErrorObject,
doesAmountErrorRequireUpdate,
} from './send.utils'
import PageContainer from '..//page-container/page-container.component'
import SendHeader from './send-header/send-header.container'
import SendContent from './send-content/send-content.component'
import SendFooter from './send-footer/send-footer.container'
export default class SendTransactionScreen extends PersistentForm {
static propTypes = {
amount: PropTypes.string,
amountConversionRate: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
conversionRate: PropTypes.number,
data: PropTypes.string,
editingTransactionId: PropTypes.string,
from: PropTypes.object,
gasLimit: PropTypes.string,
gasPrice: PropTypes.string,
gasTotal: PropTypes.string,
history: PropTypes.object,
network: PropTypes.string,
primaryCurrency: PropTypes.string,
selectedAddress: PropTypes.string,
selectedToken: PropTypes.object,
tokenBalance: PropTypes.string,
tokenContract: PropTypes.object,
updateAndSetGasTotal: PropTypes.func,
updateSendErrors: PropTypes.func,
updateSendTokenBalance: PropTypes.func,
};
updateGas () {
const {
data,
editingTransactionId,
gasLimit,
gasPrice,
selectedAddress,
selectedToken = {},
updateAndSetGasTotal,
} = this.props
updateAndSetGasTotal({
data,
editingTransactionId,
gasLimit,
gasPrice,
selectedAddress,
selectedToken,
})
}
componentDidUpdate (prevProps) {
const {
amount,
amountConversionRate,
conversionRate,
from: { address, balance },
gasTotal,
network,
primaryCurrency,
selectedToken,
tokenBalance,
updateSendErrors,
updateSendTokenBalance,
tokenContract,
} = this.props
const {
from: { balance: prevBalance },
gasTotal: prevGasTotal,
tokenBalance: prevTokenBalance,
network: prevNetwork,
} = prevProps
const uninitialized = [prevBalance, prevGasTotal].every(n => n === null)
if (!uninitialized) {
const amountErrorRequiresUpdate = doesAmountErrorRequireUpdate({
balance,
gasTotal,
prevBalance,
prevGasTotal,
prevTokenBalance,
selectedToken,
tokenBalance,
})
if (amountErrorRequiresUpdate) {
const amountErrorObject = getAmountErrorObject({
amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
})
updateSendErrors(amountErrorObject)
}
if (network !== prevNetwork && network !== 'loading') {
updateSendTokenBalance({
selectedToken,
tokenContract,
address,
})
this.updateGas()
}
}
}
componentWillMount () {
this.updateGas()
}
render () {
const { history } = this.props
return (
<PageContainer>
<SendHeader/>
<SendContent/>
<SendFooter history={history}/>
</PageContainer>
)
}
}
SendTransactionScreen.contextTypes = {
t: PropTypes.func,
}

View File

@ -0,0 +1,88 @@
import { connect } from 'react-redux'
import abi from 'ethereumjs-abi'
import SendEther from './send.component'
import { withRouter } from 'react-router-dom'
import { compose } from 'recompose'
import {
getAmountConversionRate,
getConversionRate,
getCurrentNetwork,
getGasLimit,
getGasPrice,
getGasTotal,
getPrimaryCurrency,
getSelectedAddress,
getSelectedToken,
getSelectedTokenContract,
getSelectedTokenToFiatRate,
getSendAmount,
getSendEditingTransactionId,
getSendFromObject,
getTokenBalance,
} from './send.selectors'
import {
updateSendTokenBalance,
updateGasTotal,
setGasTotal,
} from '../../actions'
import {
updateSendErrors,
} from '../../ducks/send'
import {
calcGasTotal,
generateTokenTransferData,
} from './send.utils.js'
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(SendEther)
function mapStateToProps (state) {
const selectedAddress = getSelectedAddress(state)
const selectedToken = getSelectedToken(state)
return {
amount: getSendAmount(state),
amountConversionRate: getAmountConversionRate(state),
conversionRate: getConversionRate(state),
data: generateTokenTransferData(abi, selectedAddress, selectedToken),
editingTransactionId: getSendEditingTransactionId(state),
from: getSendFromObject(state),
gasLimit: getGasLimit(state),
gasPrice: getGasPrice(state),
gasTotal: getGasTotal(state),
network: getCurrentNetwork(state),
primaryCurrency: getPrimaryCurrency(state),
selectedAddress: getSelectedAddress(state),
selectedToken: getSelectedToken(state),
tokenBalance: getTokenBalance(state),
tokenContract: getSelectedTokenContract(state),
tokenToFiatRate: getSelectedTokenToFiatRate(state),
}
}
function mapDispatchToProps (dispatch) {
return {
updateAndSetGasTotal: ({
data,
editingTransactionId,
gasLimit,
gasPrice,
selectedAddress,
selectedToken,
}) => {
!editingTransactionId
? dispatch(updateGasTotal({ selectedAddress, selectedToken, data }))
: dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice)))
},
updateSendTokenBalance: ({ selectedToken, tokenContract, address }) => {
dispatch(updateSendTokenBalance({
selectedToken,
tokenContract,
address,
}))
},
updateSendErrors: newError => dispatch(updateSendErrors(newError)),
}
}

View File

@ -8,6 +8,7 @@ const selectors = {
accountsWithSendEtherInfoSelector,
autoAddToBetaUI,
getAddressBook,
getAmountConversionRate,
getConversionRate,
getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
@ -18,6 +19,7 @@ const selectors = {
getGasLimit,
getGasPrice,
getGasTotal,
getPrimaryCurrency,
getSelectedAccount,
getSelectedAddress,
getSelectedIdentity,
@ -77,6 +79,12 @@ function getAddressBook (state) {
return state.metamask.addressBook
}
function getAmountConversionRate (state) {
return getSelectedToken(state)
? getSelectedTokenToFiatRate(state)
: getConversionRate(state)
}
function getConversionRate (state) {
return state.metamask.conversionRate
}
@ -121,6 +129,11 @@ function getGasTotal (state) {
return state.metamask.send.gasTotal
}
function getPrimaryCurrency (state) {
const selectedToken = getSelectedToken(state)
return selectedToken && selectedToken.symbol
}
function getSelectedAccount (state) {
const accounts = state.metamask.accounts
const selectedAddress = getSelectedAddress(state)
@ -161,9 +174,8 @@ function getSelectedTokenExchangeRate (state) {
const tokenExchangeRates = state.metamask.tokenExchangeRates
const selectedToken = getSelectedToken(state) || {}
const { symbol = '' } = selectedToken
const pair = `${symbol.toLowerCase()}_eth`
const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
const { rate: tokenExchangeRate = 0 } = tokenExchangeRates && tokenExchangeRates[pair] || {}
return tokenExchangeRate
}
@ -190,7 +202,7 @@ function getSendEditingTransactionId (state) {
}
function getSendErrors (state) {
return state.metamask.send.errors
return state.send.errors
}
function getSendFrom (state) {

View File

@ -7,8 +7,22 @@ const {
const {
calcTokenAmount,
} = require('../../token-util')
const {
conversionGreaterThan,
} = require('../../conversion-util')
function getGasTotal (gasLimit, gasPrice) {
module.exports = {
calcGasTotal,
doesAmountErrorRequireUpdate,
generateTokenTransferData,
getAmountErrorObject,
getParamsForGasEstimate,
calcTokenBalance,
isBalanceSufficient,
isTokenBalanceSufficient,
}
function calcGasTotal (gasLimit, gasPrice) {
return multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
@ -71,8 +85,99 @@ function isTokenBalanceSufficient ({
return tokenBalanceIsSufficient
}
module.exports = {
getGasTotal,
isBalanceSufficient,
isTokenBalanceSufficient,
function getAmountErrorObject ({
amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
}) {
let insufficientFunds = false
if (gasTotal && conversionRate) {
insufficientFunds = !isBalanceSufficient({
amount: selectedToken ? '0x0' : amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
})
}
let inSufficientTokens = false
if (selectedToken && tokenBalance !== null) {
const { decimals } = selectedToken
inSufficientTokens = !isTokenBalanceSufficient({
tokenBalance,
amount,
decimals,
})
}
const amountLessThanZero = conversionGreaterThan(
{ value: 0, fromNumericBase: 'dec' },
{ value: amount, fromNumericBase: 'hex' },
)
let amountError = null
if (insufficientFunds) {
amountError = 'insufficientFunds'
} else if (inSufficientTokens) {
amountError = 'insufficientTokens'
} else if (amountLessThanZero) {
amountError = 'negativeETH'
}
return { amount: amountError }
}
function getParamsForGasEstimate (selectedAddress, symbol, data) {
const estimatedGasParams = {
from: selectedAddress,
gas: '746a528800',
}
if (symbol) {
Object.assign(estimatedGasParams, { value: '0x0' })
}
if (data) {
Object.assign(estimatedGasParams, { data })
}
return estimatedGasParams
}
function calcTokenBalance ({ selectedToken, usersToken }) {
const { decimals } = selectedToken || {}
return calcTokenAmount(usersToken.balance.toString(), decimals)
}
function doesAmountErrorRequireUpdate ({
balance,
gasTotal,
prevBalance,
prevGasTotal,
prevTokenBalance,
selectedToken,
tokenBalance,
}) {
const balanceHasChanged = balance !== prevBalance
const gasTotalHasChange = gasTotal !== prevGasTotal
const tokenBalanceHasChanged = selectedToken && tokenBalance !== prevTokenBalance
const amountErrorRequiresUpdate = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged
return amountErrorRequiresUpdate
}
function generateTokenTransferData (abi, selectedAddress, selectedToken) {
if (!selectedToken) return
return Array.prototype.map.call(
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
x => ('00' + x.toString(16)).slice(-2)
).join('')
}

View File

@ -5,6 +5,7 @@ const OPEN_FROM_DROPDOWN = 'metamask/send/OPEN_FROM_DROPDOWN'
const CLOSE_FROM_DROPDOWN = 'metamask/send/CLOSE_FROM_DROPDOWN'
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN'
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN'
const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS'
// TODO: determine if this approach to initState is consistent with conventional ducks pattern
const initState = {
@ -32,6 +33,13 @@ export default function reducer ({ send: sendState = initState }, action = {}) {
return extend(sendState, {
toDropdownOpen: false,
})
case UPDATE_SEND_ERRORS:
return extend(sendState, {
errors: {
...sendState.errors,
...action.value,
},
})
default:
return sendState
}
@ -53,3 +61,10 @@ export function openToDropdown () {
export function closeToDropdown () {
return { type: CLOSE_TO_DROPDOWN }
}
export function updateSendErrors (errorObject) {
return {
type: UPDATE_SEND_ERRORS,
value: errorObject,
}
}

View File

@ -254,17 +254,6 @@ function reduceMetamask (state, action) {
},
})
case actions.UPDATE_SEND_ERRORS:
return extend(metamaskState, {
send: {
...metamaskState.send,
errors: {
...metamaskState.send.errors,
...action.value,
},
},
})
case actions.UPDATE_MAX_MODE:
return extend(metamaskState, {
send: {

View File

@ -84,7 +84,7 @@ SendTransactionScreen.prototype.updateGas = function () {
estimateGas,
selectedAddress,
data,
updateGasTotal,
setGasTotal,
from,
tokenContract,
editingTransactionId,
@ -110,7 +110,7 @@ SendTransactionScreen.prototype.updateGas = function () {
])
.then(([gasPrice, gas]) => {
const newGasTotal = getGasTotal(gas, gasPrice)
updateGasTotal(newGasTotal)
setGasTotal(newGasTotal)
this.setState({ gasLoadingError: false })
})
.catch(err => {
@ -118,7 +118,7 @@ SendTransactionScreen.prototype.updateGas = function () {
})
} else {
const newGasTotal = getGasTotal(gasLimit, gasPrice)
updateGasTotal(newGasTotal)
setGasTotal(newGasTotal)
}
}