mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Further refactors; includes refactor of send-v2.js and associated container
This commit is contained in:
parent
33c16d1bf6
commit
26f965bcce
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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 }),
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)),
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -10,6 +10,7 @@ export default class SendRowErrorMessage extends Component {
|
||||
|
||||
render () {
|
||||
const { errors, errorType } = this.props
|
||||
|
||||
const errorMessage = errors[errorType]
|
||||
|
||||
return (
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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,
|
||||
}
|
@ -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)),
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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('')
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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: {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user