mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Core of the refactor complete
This commit is contained in:
parent
02a6d2089e
commit
8ff7806f1b
@ -8,6 +8,7 @@ export default class PageContainer extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
console.log(`QQQQQQQQQQQQQQQQQ this.props.children`, this.props.children);
|
||||||
return (
|
return (
|
||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { checksumAddress } from '../../../util'
|
||||||
|
import Identicon from '../../identicon'
|
||||||
|
import CurrencyDisplay from '../../send/currency-display'
|
||||||
|
|
||||||
|
export default class AccountListItem extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: PropTypes.object,
|
||||||
|
className: PropTypes.string,
|
||||||
|
conversionRate: PropTypes.number,
|
||||||
|
currentCurrency: PropTypes.string,
|
||||||
|
displayAddress: PropTypes.bool,
|
||||||
|
displayBalance: PropTypes.bool,
|
||||||
|
handleClick: PropTypes.func,
|
||||||
|
icon: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
account,
|
||||||
|
handleClick,
|
||||||
|
icon = null,
|
||||||
|
conversionRate,
|
||||||
|
currentCurrency,
|
||||||
|
displayBalance = true,
|
||||||
|
displayAddress = false,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const { name, address, balance } = account || {}
|
||||||
|
|
||||||
|
return (<div
|
||||||
|
className={`account-list-item ${className}`}
|
||||||
|
onClick={() => handleClick({ name, address, balance })}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className='account-list-item__top-row'>
|
||||||
|
<Identicon
|
||||||
|
address={address}
|
||||||
|
diameter={18}
|
||||||
|
className='account-list-item__identicon'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='account-list-item__account-name'>{ name || address }</div>
|
||||||
|
|
||||||
|
{icon && <div className='account-list-item__icon'>{ icon }</div>}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{displayAddress && name && <div className='account-list-item__account-address'>
|
||||||
|
{ checksumAddress(address) }
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{displayBalance && <CurrencyDisplay
|
||||||
|
primaryCurrency='ETH'
|
||||||
|
convertedCurrency={currentCurrency}
|
||||||
|
value={balance}
|
||||||
|
conversionRate={conversionRate}
|
||||||
|
readOnly={true}
|
||||||
|
className='account-list-item__account-balances'
|
||||||
|
primaryBalanceClassName='account-list-item__account-primary-balance'
|
||||||
|
convertedBalanceClassName='account-list-item__account-secondary-balance'
|
||||||
|
/>}
|
||||||
|
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountListItem.contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import {
|
||||||
|
getConversionRate,
|
||||||
|
getConvertedCurrency,
|
||||||
|
} from '../send.selectors.js'
|
||||||
|
import AccountListItem from './account-list-item.component'
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(AccountListItem)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
conversionRate: getConversionRate(state),
|
||||||
|
currentCurrency: getConvertedCurrency(state),
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ export default class AmountMaxButton extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { setMaxModeTo } = this.props
|
const { setMaxModeTo, maxModeOn } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -42,7 +42,7 @@ export default class AmountMaxButton extends Component {
|
|||||||
this.setAmountToMax()
|
this.setAmountToMax()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!maxModeOn ? this.context.t('max') : '' ])}
|
{!maxModeOn ? this.context.t('max') : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getSelectedToken,
|
getSelectedToken,
|
||||||
getGasTotal,
|
getGasTotal,
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
||||||
import AmountMaxButton from '../amount-max-button/amount-max-button.component'
|
import AmountMaxButton from './amount-max-button/amount-max-button.component'
|
||||||
import CurrencyDisplay from '../../../send/currency-display'
|
import CurrencyDisplay from '../../../send/currency-display'
|
||||||
|
|
||||||
export default class SendAmountRow extends Component {
|
export default class SendAmountRow extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
amountConversionRate: PropTypes.string,
|
amountConversionRate: PropTypes.number,
|
||||||
conversionRate: PropTypes.string,
|
conversionRate: PropTypes.number,
|
||||||
from: PropTypes.object,
|
|
||||||
gasTotal: PropTypes.string,
|
gasTotal: PropTypes.string,
|
||||||
primaryCurrency: PropTypes.string,
|
primaryCurrency: PropTypes.string,
|
||||||
selectedToken: PropTypes.object,
|
selectedToken: PropTypes.object,
|
||||||
@ -23,7 +22,7 @@ export default class SendAmountRow extends Component {
|
|||||||
const {
|
const {
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
from: { balance },
|
balance,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
@ -69,16 +68,16 @@ export default class SendAmountRow extends Component {
|
|||||||
showError={inError}
|
showError={inError}
|
||||||
errorType={'amount'}
|
errorType={'amount'}
|
||||||
>
|
>
|
||||||
!inError && gasTotal && <AmountMaxButton />
|
{!inError && gasTotal && <AmountMaxButton />}
|
||||||
<CurrencyDisplay
|
<CurrencyDisplay
|
||||||
inError={inError},
|
inError={inError}
|
||||||
primaryCurrency={primaryCurrency},
|
primaryCurrency={primaryCurrency}
|
||||||
convertedCurrency={convertedCurrency},
|
convertedCurrency={convertedCurrency}
|
||||||
selectedToken={selectedToken},
|
selectedToken={selectedToken}
|
||||||
value={amount || '0x0'},
|
value={amount || '0x0'}
|
||||||
conversionRate={amountConversionRate},
|
conversionRate={amountConversionRate}
|
||||||
handleChange={this.handleAmountChange},
|
handleChange={newAmount => this.handleAmountChange(newAmount)}
|
||||||
>
|
/>
|
||||||
</SendRowWrapper>
|
</SendRowWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getSelectedToken,
|
getSelectedToken,
|
||||||
getPrimaryCurrency,
|
|
||||||
getAmountConversionRate,
|
|
||||||
getConvertedCurrency,
|
getConvertedCurrency,
|
||||||
getSendAmount,
|
getSendAmount,
|
||||||
getGasTotal,
|
getGasTotal,
|
||||||
getSelectedBalance,
|
getSelectedBalance,
|
||||||
getTokenBalance,
|
getTokenBalance,
|
||||||
getSendFromBalance,
|
getSendFromBalance,
|
||||||
|
getConversionRate,
|
||||||
} from '../../send.selectors.js'
|
} from '../../send.selectors.js'
|
||||||
import {
|
import {
|
||||||
getMaxModeOn,
|
getMaxModeOn,
|
||||||
sendAmountIsInError,
|
sendAmountIsInError,
|
||||||
|
getPrimaryCurrency,
|
||||||
|
getAmountConversionRate,
|
||||||
} from './send-amount-row.selectors.js'
|
} from './send-amount-row.selectors.js'
|
||||||
import { getAmountErrorObject } from './send-amount-row.utils.js'
|
import { getAmountErrorObject } from './send-amount-row.utils.js'
|
||||||
import {
|
import {
|
||||||
updateSendAmount,
|
updateSendAmount,
|
||||||
setMaxModeTo,
|
setMaxModeTo,
|
||||||
} from '../../../actions'
|
updateSendErrors,
|
||||||
|
} from '../../../../actions'
|
||||||
import SendAmountRow from './send-amount-row.component'
|
import SendAmountRow from './send-amount-row.component'
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(SendToRow)
|
export default connect(mapStateToProps, mapDispatchToProps)(SendAmountRow)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
updateSendTo
|
return {
|
||||||
return {
|
|
||||||
selectedToken: getSelectedToken(state),
|
selectedToken: getSelectedToken(state),
|
||||||
primaryCurrency: getPrimaryCurrency(state),
|
primaryCurrency: getPrimaryCurrency(state),
|
||||||
convertedCurrency: getConvertedCurrency(state),
|
convertedCurrency: getConvertedCurrency(state),
|
||||||
@ -35,7 +37,8 @@ return {
|
|||||||
gasTotal: getGasTotal(state),
|
gasTotal: getGasTotal(state),
|
||||||
tokenBalance: getTokenBalance(state),
|
tokenBalance: getTokenBalance(state),
|
||||||
balance: getSendFromBalance(state),
|
balance: getSendFromBalance(state),
|
||||||
}
|
conversionRate: getConversionRate(state),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
|
import {
|
||||||
|
getSelectedToken,
|
||||||
|
getSelectedTokenToFiatRate,
|
||||||
|
getConversionRate,
|
||||||
|
} from '../../send.selectors.js'
|
||||||
|
|
||||||
const selectors = {
|
const selectors = {
|
||||||
getMaxModeOn,
|
getMaxModeOn,
|
||||||
sendAmountIsInError,
|
sendAmountIsInError,
|
||||||
|
getPrimaryCurrency,
|
||||||
|
getAmountConversionRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
@ -12,3 +20,14 @@ function getMaxModeOn (state) {
|
|||||||
function sendAmountIsInError (state) {
|
function sendAmountIsInError (state) {
|
||||||
return Boolean(state.metamask.send.errors.amount)
|
return Boolean(state.metamask.send.errors.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPrimaryCurrency (state) {
|
||||||
|
const selectedToken = getSelectedToken(state)
|
||||||
|
return selectedToken && selectedToken.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAmountConversionRate (state) {
|
||||||
|
return Boolean(getSelectedToken(state))
|
||||||
|
? getSelectedTokenToFiatRate(state)
|
||||||
|
: getConversionRate(state)
|
||||||
|
}
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
const { isValidAddress } = require('../../../../util')
|
const { isValidAddress } = require('../../../../util')
|
||||||
|
const {
|
||||||
|
conversionGreaterThan,
|
||||||
|
} = require('../../../../conversion-util')
|
||||||
|
const {
|
||||||
|
isBalanceSufficient,
|
||||||
|
isTokenBalanceSufficient,
|
||||||
|
} = require('../../send.utils')
|
||||||
|
|
||||||
function getAmountErrorObject ({
|
function getAmountErrorObject ({
|
||||||
amount,
|
amount,
|
||||||
@ -10,6 +17,14 @@ function getAmountErrorObject ({
|
|||||||
gasTotal,
|
gasTotal,
|
||||||
tokenBalance,
|
tokenBalance,
|
||||||
}) {
|
}) {
|
||||||
|
console.log(`#& getAmountErrorObject amount`, amount);
|
||||||
|
console.log(`#& getAmountErrorObject balance`, balance);
|
||||||
|
console.log(`#& getAmountErrorObject amountConversionRate`, amountConversionRate);
|
||||||
|
console.log(`#& getAmountErrorObject conversionRate`, conversionRate);
|
||||||
|
console.log(`#& getAmountErrorObject primaryCurrency`, primaryCurrency);
|
||||||
|
console.log(`#& getAmountErrorObject selectedToken`, selectedToken);
|
||||||
|
console.log(`#& getAmountErrorObject gasTotal`, gasTotal);
|
||||||
|
console.log(`#& getAmountErrorObject tokenBalance`, tokenBalance);
|
||||||
let insufficientFunds = false
|
let insufficientFunds = false
|
||||||
if (gasTotal && conversionRate) {
|
if (gasTotal && conversionRate) {
|
||||||
insufficientFunds = !isBalanceSufficient({
|
insufficientFunds = !isBalanceSufficient({
|
||||||
@ -40,11 +55,11 @@ function getAmountErrorObject ({
|
|||||||
let amountError = null
|
let amountError = null
|
||||||
|
|
||||||
if (insufficientFunds) {
|
if (insufficientFunds) {
|
||||||
amountError = this.context.t('insufficientFunds')
|
amountError = 'insufficientFunds'
|
||||||
} else if (insufficientTokens) {
|
} else if (inSufficientTokens) {
|
||||||
amountError = this.context.t('insufficientTokens')
|
amountError = 'insufficientTokens'
|
||||||
} else if (amountLessThanZero) {
|
} else if (amountLessThanZero) {
|
||||||
amountError = this.context.t('negativeETH')
|
amountError = 'negativeETH'
|
||||||
}
|
}
|
||||||
|
|
||||||
return { amount: amountError }
|
return { amount: amountError }
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PageContainerContent from '../../page-container/page-container-header.component'
|
import PageContainerContent from '../../page-container/page-container-content.component'
|
||||||
import SendFromRow from './send-from-row/send-from-row.component'
|
import SendFromRow from './send-from-row/send-from-row.container'
|
||||||
import SendToRow from './send-to-row/send-to-row.component'
|
import SendToRow from './send-to-row/send-to-row.container'
|
||||||
import SendAmountRow from './send-amount-row/send-amount-row.component'
|
import SendAmountRow from './send-amount-row/send-amount-row.container'
|
||||||
import SendGasRow from './send-gas-row/send-gas-row.component'
|
import SendGasRow from './send-gas-row/send-gas-row.container'
|
||||||
|
|
||||||
export default class SendContent extends Component {
|
export default class SendContent extends Component {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
console.log('111222333444555666777888999')
|
||||||
return (
|
return (
|
||||||
<PageContainerContent>
|
<PageContainerContent>
|
||||||
<div className='.send-v2__form'>
|
<div className='.send-v2__form'>
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import AccountListItem from '../../../account-list-item/account-list-item.container'
|
||||||
|
|
||||||
|
export default class FromDropdown extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
accounts: PropTypes.array,
|
||||||
|
closeDropdown: PropTypes.func,
|
||||||
|
dropdownOpen: PropTypes.bool,
|
||||||
|
onSelect: PropTypes.func,
|
||||||
|
openDropdown: PropTypes.func,
|
||||||
|
selectedAccount: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderListItemIcon (icon, color) {
|
||||||
|
return <i className={`fa ${icon} fa-lg`} style={ { color } }/>
|
||||||
|
}
|
||||||
|
|
||||||
|
getListItemIcon (currentAccount, selectedAccount) {
|
||||||
|
return currentAccount.address === selectedAccount.address
|
||||||
|
? this.renderListItemIcon('fa-check', '#02c9b1')
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDropdown () {
|
||||||
|
const {
|
||||||
|
accounts,
|
||||||
|
selectedAccount,
|
||||||
|
closeDropdown,
|
||||||
|
onSelect,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (<div>
|
||||||
|
<div
|
||||||
|
className='send-v2__from-dropdown__close-area'
|
||||||
|
onClick={() => closeDropdown}
|
||||||
|
/>
|
||||||
|
<div className='send-v2__from-dropdown__list'>
|
||||||
|
{...accounts.map(account => <AccountListItem
|
||||||
|
className='account-list-item__dropdown'
|
||||||
|
account={account}
|
||||||
|
handleClick={() => {
|
||||||
|
onSelect(account)
|
||||||
|
closeDropdown()
|
||||||
|
}}
|
||||||
|
icon={this.getListItemIcon(account, selectedAccount.address)}
|
||||||
|
/>)}
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
selectedAccount,
|
||||||
|
openDropdown,
|
||||||
|
dropdownOpen,
|
||||||
|
} = this.props
|
||||||
|
console.log(`&*& openDropdown`, openDropdown);
|
||||||
|
console.log(`&*& dropdownOpen`, dropdownOpen);
|
||||||
|
return <div className='send-v2__from-dropdown'>
|
||||||
|
<AccountListItem
|
||||||
|
account={selectedAccount}
|
||||||
|
handleClick={openDropdown}
|
||||||
|
icon={this.renderListItemIcon('fa-caret-down', '#dedede')}
|
||||||
|
/>
|
||||||
|
{dropdownOpen && this.renderDropdown()},
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FromDropdown.contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
||||||
import FromDropdown from '../../../send/from-dropdown'
|
import FromDropdown from './from-dropdown/from-dropdown.component'
|
||||||
|
|
||||||
export default class SendFromRow extends Component {
|
export default class SendFromRow extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
closeFromDropdown: PropTypes.func,
|
closeFromDropdown: PropTypes.func,
|
||||||
conversionRate: PropTypes.string,
|
conversionRate: PropTypes.number,
|
||||||
from: PropTypes.string,
|
from: PropTypes.object,
|
||||||
fromAccounts: PropTypes.array,
|
fromAccounts: PropTypes.array,
|
||||||
fromDropdownOpen: PropTypes.bool,
|
fromDropdownOpen: PropTypes.bool,
|
||||||
openFromDropdown: PropTypes.func,
|
openFromDropdown: PropTypes.func,
|
||||||
@ -41,7 +41,7 @@ export default class SendFromRow extends Component {
|
|||||||
openFromDropdown,
|
openFromDropdown,
|
||||||
closeFromDropdown,
|
closeFromDropdown,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
console.log(`$% SendFromRow fromAccounts`, fromAccounts);
|
||||||
return (
|
return (
|
||||||
<SendRowWrapper label={`${this.context.t('from')}:`}>
|
<SendRowWrapper label={`${this.context.t('from')}:`}>
|
||||||
<FromDropdown
|
<FromDropdown
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getSendFrom,
|
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
getSelectedTokenContract,
|
getSelectedTokenContract,
|
||||||
getCurrentAccountWithSendEtherInfo,
|
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
|
getSendFromObject,
|
||||||
} from '../../send.selectors.js'
|
} from '../../send.selectors.js'
|
||||||
import { getFromDropdownOpen } from './send-from-row.selectors.js'
|
import {
|
||||||
|
getFromDropdownOpen,
|
||||||
|
} from './send-from-row.selectors.js'
|
||||||
import { calcTokenUpdateAmount } from './send-from-row.utils.js'
|
import { calcTokenUpdateAmount } from './send-from-row.utils.js'
|
||||||
import {
|
import {
|
||||||
updateSendTokenBalance,
|
updateSendTokenBalance,
|
||||||
updateSendFrom,
|
updateSendFrom,
|
||||||
} from '../../../actions'
|
} from '../../../../actions'
|
||||||
import {
|
import {
|
||||||
openFromDropdown,
|
openFromDropdown,
|
||||||
closeFromDropdown,
|
closeFromDropdown,
|
||||||
} from '../../../ducks/send'
|
} from '../../../../ducks/send'
|
||||||
import SendFromRow from './send-from-row.component'
|
import SendFromRow from './send-from-row.component'
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow)
|
export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
console.log(`$% mapStateToProps accountsWithSendEtherInfoSelector`, accountsWithSendEtherInfoSelector);
|
||||||
return {
|
return {
|
||||||
from: getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state),
|
from: getSendFromObject(state),
|
||||||
fromAccounts: accountsWithSendEtherInfoSelector(state),
|
fromAccounts: accountsWithSendEtherInfoSelector(state),
|
||||||
conversionRate: getConversionRate(state),
|
conversionRate: getConversionRate(state),
|
||||||
fromDropdownOpen: getFromDropdownOpen(state),
|
fromDropdownOpen: getFromDropdownOpen(state),
|
||||||
@ -38,7 +41,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
dispatch(updateSendTokenBalance(tokenBalance))
|
dispatch(updateSendTokenBalance(tokenBalance))
|
||||||
},
|
},
|
||||||
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)),
|
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)),
|
||||||
openFromDropdown: () => dispatch(()),
|
openFromDropdown: () => dispatch(openFromDropdown()),
|
||||||
closeFromDropdown: () => dispatch(()),
|
closeFromDropdown: () => dispatch(closeFromDropdown()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const {
|
const {
|
||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
} = require('../../token-util')
|
} = require('../../../../token-util')
|
||||||
|
|
||||||
function calcTokenUpdateAmount (usersToken, selectedToken) {
|
function calcTokenUpdateAmount (usersToken, selectedToken) {
|
||||||
const { decimals } = selectedToken || {}
|
const { decimals } = selectedToken || {}
|
||||||
|
@ -7,7 +7,7 @@ export default class SendGasRow extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
closeFromDropdown: PropTypes.func,
|
closeFromDropdown: PropTypes.func,
|
||||||
conversionRate: PropTypes.string,
|
conversionRate: PropTypes.number,
|
||||||
from: PropTypes.string,
|
from: PropTypes.string,
|
||||||
fromAccounts: PropTypes.array,
|
fromAccounts: PropTypes.array,
|
||||||
fromDropdownOpen: PropTypes.bool,
|
fromDropdownOpen: PropTypes.bool,
|
||||||
@ -15,6 +15,7 @@ export default class SendGasRow extends Component {
|
|||||||
tokenContract: PropTypes.object,
|
tokenContract: PropTypes.object,
|
||||||
updateSendFrom: PropTypes.func,
|
updateSendFrom: PropTypes.func,
|
||||||
updateSendTokenBalance: PropTypes.func,
|
updateSendTokenBalance: PropTypes.func,
|
||||||
|
gasLoadingError: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
async handleFromChange (newFrom) {
|
async handleFromChange (newFrom) {
|
||||||
@ -43,11 +44,11 @@ export default class SendGasRow extends Component {
|
|||||||
return (
|
return (
|
||||||
<SendRowWrapper label={`${this.context.t('gasFee')}:`}>
|
<SendRowWrapper label={`${this.context.t('gasFee')}:`}>
|
||||||
<GasFeeDisplay
|
<GasFeeDisplay
|
||||||
gasTotal={gasTotal},
|
gasTotal={gasTotal}
|
||||||
conversionRate={conversionRate},
|
conversionRate={conversionRate}
|
||||||
convertedCurrency={convertedCurrency},
|
convertedCurrency={convertedCurrency}
|
||||||
onClick={() => showCustomizeGasModal()},
|
onClick={() => showCustomizeGasModal()}
|
||||||
gasLoadingError={gasLoadingError},
|
gasLoadingError={gasLoadingError}
|
||||||
/>
|
/>
|
||||||
</SendRowWrapper>
|
</SendRowWrapper>
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
getConvertedCurrency,
|
getConvertedCurrency,
|
||||||
getGasTotal,
|
getGasTotal,
|
||||||
} from '../../send.selectors.js'
|
} from '../../send.selectors.js'
|
||||||
import { getGasLoadingError } from './send-gas-row.selectors.js'
|
import { sendGasIsInError } from './send-gas-row.selectors.js'
|
||||||
import { calcTokenUpdateAmount } from './send-gas-row.utils.js'
|
import { showModal } from '../../../../actions'
|
||||||
import { showModal } from '../../../actions'
|
import SendGasRow from './send-gas-row.component'
|
||||||
import SendGasRow from './send-from-row.component'
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
|
export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ function mapStateToProps (state) {
|
|||||||
conversionRate: getConversionRate(state),
|
conversionRate: getConversionRate(state),
|
||||||
convertedCurrency: getConvertedCurrency(state),
|
convertedCurrency: getConvertedCurrency(state),
|
||||||
gasTotal: getGasTotal(state),
|
gasTotal: getGasTotal(state),
|
||||||
gasLoadingError: getGasLoadingError(state),
|
gasLoadingError: sendGasIsInError(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,5 +5,5 @@ const selectors = {
|
|||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
|
|
||||||
function sendGasIsInError (state) {
|
function sendGasIsInError (state) {
|
||||||
return state.send.errors.gasLoading
|
return state.metamask.send.errors.gasLoading
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
export default class SendRowErrorMessage extends Component {
|
export default class SendRowErrorMessage extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -11,7 +14,7 @@ export default class SendRowErrorMessage extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
errorMessage
|
errorMessage
|
||||||
? <div className='send-v2__error'>{errorMessage}</div>
|
? <div className='send-v2__error'>{this.context.t(errorMessage)}</div>
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import { getSendErrors } from '../../../send.selectors'
|
import { getSendErrors } from '../../../send.selectors'
|
||||||
import SendRowErrorMessage from './send-row-error-message.component'
|
import SendRowErrorMessage from './send-row-error-message.component'
|
||||||
|
|
||||||
|
@ -19,19 +19,14 @@ export default class SendRowWrapper extends Component {
|
|||||||
children,
|
children,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
let formField = children[0]
|
let formField = Array.isArray(children) ? children[1] || children[0] : children
|
||||||
let customLabelContent = null
|
let customLabelContent = children.length === 1 ? children[0] : null
|
||||||
|
|
||||||
if (children.length === 2) {
|
|
||||||
formField = children[1]
|
|
||||||
customLabelContent = children[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="send-v2__form-row">
|
<div className="send-v2__form-row">
|
||||||
<div className="send-v2__form-label">
|
<div className="send-v2__form-label">
|
||||||
{label}
|
{label}
|
||||||
(showError && <SendRowErrorMessage errorType={errorType}/>)
|
{showError && <SendRowErrorMessage errorType={errorType}/>}
|
||||||
{customLabelContent}
|
{customLabelContent}
|
||||||
</div>
|
</div>
|
||||||
<div className="send-v2__form-field">
|
<div className="send-v2__form-field">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component'
|
||||||
import ToDropdown from '../../../ens-input'
|
import EnsInput from '../../../ens-input'
|
||||||
|
|
||||||
export default class SendToRow extends Component {
|
export default class SendToRow extends Component {
|
||||||
|
|
||||||
@ -14,19 +14,20 @@ export default class SendToRow extends Component {
|
|||||||
updateSendToError: PropTypes.func,
|
updateSendToError: PropTypes.func,
|
||||||
openToDropdown: PropTypes.func,
|
openToDropdown: PropTypes.func,
|
||||||
closeToDropdown: PropTypes.func,
|
closeToDropdown: PropTypes.func,
|
||||||
network: PropTypes.number,
|
network: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleToChange (to, nickname = '') {
|
handleToChange (to, nickname = '') {
|
||||||
const { updateSendTo, updateSendToError } = this.props
|
const { updateSendTo, updateSendToError } = this.props
|
||||||
updateSendTo(to, nickname)
|
updateSendTo(to, nickname)
|
||||||
updateSendErrors(to)
|
updateSendToError(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
from,
|
from,
|
||||||
fromAccounts,
|
fromAccounts,
|
||||||
|
toAccounts,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
fromDropdownOpen,
|
fromDropdownOpen,
|
||||||
tokenContract,
|
tokenContract,
|
||||||
@ -34,6 +35,8 @@ export default class SendToRow extends Component {
|
|||||||
closeToDropdown,
|
closeToDropdown,
|
||||||
network,
|
network,
|
||||||
inError,
|
inError,
|
||||||
|
to,
|
||||||
|
toDropdownOpen,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,14 +47,14 @@ export default class SendToRow extends Component {
|
|||||||
>
|
>
|
||||||
<EnsInput
|
<EnsInput
|
||||||
name={'address'}
|
name={'address'}
|
||||||
placeholder={this.context.t('recipient Address')}
|
placeholder={this.context.t('recipientAddress')}
|
||||||
network={network},
|
network={network}
|
||||||
to={to},
|
to={to}
|
||||||
accounts={toAccounts}
|
accounts={toAccounts}
|
||||||
dropdownOpen={toDropdownOpen}
|
dropdownOpen={toDropdownOpen}
|
||||||
openDropdown={() => openToDropdown()}
|
openDropdown={() => openToDropdown()}
|
||||||
closeDropdown={() => closeToDropdown()}
|
closeDropdown={() => closeToDropdown()}
|
||||||
onChange={this.handleToChange}
|
onChange={(newTo, newNickname) => this.handleToChange(newTo, newNickname)}
|
||||||
inError={inError}
|
inError={inError}
|
||||||
/>
|
/>
|
||||||
</SendRowWrapper>
|
</SendRowWrapper>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
getSendTo,
|
getSendTo,
|
||||||
getToAccounts,
|
getToAccounts,
|
||||||
getCurrentNetwork,
|
getCurrentNetwork,
|
||||||
|
getSendToAccounts,
|
||||||
} from '../../send.selectors.js'
|
} from '../../send.selectors.js'
|
||||||
import {
|
import {
|
||||||
getToDropdownOpen,
|
getToDropdownOpen,
|
||||||
@ -11,11 +13,11 @@ import { getToErrorObject } from './send-to-row.utils.js'
|
|||||||
import {
|
import {
|
||||||
updateSendErrors,
|
updateSendErrors,
|
||||||
updateSendTo,
|
updateSendTo,
|
||||||
} from '../../../actions'
|
} from '../../../../actions'
|
||||||
import {
|
import {
|
||||||
openToDropdown,
|
openToDropdown,
|
||||||
closeToDropdown,
|
closeToDropdown,
|
||||||
} from '../../../ducks/send'
|
} from '../../../../ducks/send'
|
||||||
import SendToRow from './send-to-row.component'
|
import SendToRow from './send-to-row.component'
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(SendToRow)
|
export default connect(mapStateToProps, mapDispatchToProps)(SendToRow)
|
||||||
@ -37,7 +39,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
dispatch(updateSendErrors(getToErrorObject(to)))
|
dispatch(updateSendErrors(getToErrorObject(to)))
|
||||||
},
|
},
|
||||||
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
|
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
|
||||||
openToDropdown: () => dispatch(()),
|
openToDropdown: () => dispatch(openToDropdown()),
|
||||||
closeToDropdown: () => dispatch(()),
|
closeToDropdown: () => dispatch(closeToDropdown()),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import PageContainerFooter from '../../page-container/page-container-footer.component'
|
||||||
|
import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE } from '../../../routes'
|
||||||
|
|
||||||
|
export default class SendFooter extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
addToAddressBook: PropTypes.func,
|
||||||
|
amount: PropTypes.string,
|
||||||
|
clearSend: PropTypes.func,
|
||||||
|
editingTransactionId: PropTypes.string,
|
||||||
|
errors: PropTypes.object,
|
||||||
|
from: PropTypes.object,
|
||||||
|
gasLimit: PropTypes.string,
|
||||||
|
gasPrice: PropTypes.string,
|
||||||
|
gasTotal: PropTypes.string,
|
||||||
|
history: PropTypes.object,
|
||||||
|
selectedToken: PropTypes.object,
|
||||||
|
signTokenTx: PropTypes.func,
|
||||||
|
signTx: PropTypes.func,
|
||||||
|
to: PropTypes.string,
|
||||||
|
toAccounts: PropTypes.array,
|
||||||
|
tokenBalance: PropTypes.string,
|
||||||
|
unapprovedTxs: PropTypes.object,
|
||||||
|
updateTx: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
onSubmit (event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const {
|
||||||
|
addToAddressBookIfNew,
|
||||||
|
amount,
|
||||||
|
editingTransactionId,
|
||||||
|
from: {address: from},
|
||||||
|
gasLimit: gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
sign,
|
||||||
|
to,
|
||||||
|
unapprovedTxs,
|
||||||
|
// updateTx,
|
||||||
|
update,
|
||||||
|
toAccounts,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
// Should not be needed because submit should be disabled if there are no errors.
|
||||||
|
// const noErrors = !amountError && toError === null
|
||||||
|
|
||||||
|
// if (!noErrors) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: add nickname functionality
|
||||||
|
addToAddressBookIfNew(to, toAccounts)
|
||||||
|
|
||||||
|
editingTransactionId
|
||||||
|
? update({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
editingTransactionId,
|
||||||
|
unapprovedTxs,
|
||||||
|
})
|
||||||
|
: sign({ selectedToken, to, amount, from, gas, gasPrice })
|
||||||
|
|
||||||
|
this.props.history.push(CONFIRM_TRANSACTION_ROUTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { clearSend, disabled, history } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainerFooter
|
||||||
|
onCancel={() => {
|
||||||
|
clearSend()
|
||||||
|
history.push(DEFAULT_ROUTE)
|
||||||
|
}}
|
||||||
|
onSubmit={e => this.onSubmit(e)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SendFooter.contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import ethUtil from 'ethereumjs-util'
|
||||||
|
import {
|
||||||
|
addToAddressBook,
|
||||||
|
clearSend,
|
||||||
|
goHome,
|
||||||
|
signTokenTx,
|
||||||
|
signTx,
|
||||||
|
updateTransaction,
|
||||||
|
} from '../../../actions'
|
||||||
|
import SendFooter from './send-footer.component'
|
||||||
|
import {
|
||||||
|
getGasLimit,
|
||||||
|
getGasPrice,
|
||||||
|
getGasTotal,
|
||||||
|
getSelectedToken,
|
||||||
|
getSendAmount,
|
||||||
|
getSendEditingTransactionId,
|
||||||
|
getSendFromObject,
|
||||||
|
getSendTo,
|
||||||
|
getSendToAccounts,
|
||||||
|
getTokenBalance,
|
||||||
|
getUnapprovedTxs,
|
||||||
|
} from '../send.selectors'
|
||||||
|
import {
|
||||||
|
isSendFormInError,
|
||||||
|
} from './send-footer.selectors'
|
||||||
|
import {
|
||||||
|
addressIsNew,
|
||||||
|
formShouldBeDisabled,
|
||||||
|
constructTxParams,
|
||||||
|
} from './send-footer.utils'
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(SendFooter)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
isToken: Boolean(getSelectedToken(state)),
|
||||||
|
inError: isSendFormInError(state),
|
||||||
|
disabled: formShouldBeDisabled({
|
||||||
|
inError: isSendFormInError(state),
|
||||||
|
selectedToken: getSelectedToken(state),
|
||||||
|
tokenBalance: getTokenBalance(state),
|
||||||
|
gasTotal: getGasTotal(state),
|
||||||
|
}),
|
||||||
|
amount: getSendAmount(state),
|
||||||
|
editingTransactionId: getSendEditingTransactionId(state),
|
||||||
|
from: getSendFromObject(state),
|
||||||
|
gasLimit: getGasLimit(state),
|
||||||
|
gasPrice: getGasPrice(state),
|
||||||
|
selectedToken: getSelectedToken(state),
|
||||||
|
to: getSendTo(state),
|
||||||
|
unapprovedTxs: getUnapprovedTxs(state),
|
||||||
|
toAccounts: getSendToAccounts(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return {
|
||||||
|
goHome: () => dispatch(goHome()),
|
||||||
|
clearSend: () => dispatch(clearSend()),
|
||||||
|
sign: ({ selectedToken, to, amount, from, gas, gasPrice }) => {
|
||||||
|
const txParams = constructTxParams({
|
||||||
|
amount,
|
||||||
|
from,
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
to,
|
||||||
|
})
|
||||||
|
|
||||||
|
selectedToken
|
||||||
|
? dispatch(signTokenTx(selectedToken.address, to, amount, txParams))
|
||||||
|
: dispatch(signTx(txParams))
|
||||||
|
},
|
||||||
|
update: ({
|
||||||
|
amount,
|
||||||
|
editingTransactionId,
|
||||||
|
from,
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
to,
|
||||||
|
unapprovedTxs,
|
||||||
|
}) => {
|
||||||
|
const editingTx = constructUpdatedTx({
|
||||||
|
amount,
|
||||||
|
editingTransactionId,
|
||||||
|
from,
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
to,
|
||||||
|
unapprovedTxs,
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(updateTransaction(editingTx))
|
||||||
|
},
|
||||||
|
addToAddressBookIfNew: (newAddress, toAccounts, nickname = '') => {
|
||||||
|
const hexPrefixedAddress = ethUtil.addHexPrefix(newAddress)
|
||||||
|
if (addressIsNew(toAccounts)) {
|
||||||
|
// TODO: nickname, i.e. addToAddressBook(recipient, nickname)
|
||||||
|
dispatch(addToAddressBook(hexPrefixedAddress, nickname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { getSendErrors } from '../send.selectors'
|
||||||
|
|
||||||
|
const selectors = {
|
||||||
|
isSendFormInError,
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = selectors
|
||||||
|
|
||||||
|
function isSendFormInError (state) {
|
||||||
|
const { amount, to } = getSendErrors(state)
|
||||||
|
return Boolean(amount || to !== null)
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import ethAbi from 'ethereumjs-abi'
|
||||||
|
import ethUtil from 'ethereumjs-util'
|
||||||
|
import { TOKEN_TRANSFER_FUNCTION_SIGNATURE } from '../send.constants'
|
||||||
|
|
||||||
|
function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) {
|
||||||
|
const missingTokenBalance = selectedToken && !tokenBalance
|
||||||
|
return inError || !gasTotal || missingTokenBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHexPrefixToObjectValues (obj) {
|
||||||
|
return Object.keys(obj).reduce((newObj, key) => {
|
||||||
|
return { ...newObj, [key]: ethUtil.addHexPrefix(obj[key]) }
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice }) {
|
||||||
|
const txParams = {
|
||||||
|
from,
|
||||||
|
value: '0',
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedToken) {
|
||||||
|
txParams.value = amount
|
||||||
|
txParams.to = to
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexPrefixedTxParams = addHexPrefixToObjectValues(txParams)
|
||||||
|
|
||||||
|
return hexPrefixedTxParams
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructUpdatedTx ({
|
||||||
|
amount,
|
||||||
|
editingTransactionId,
|
||||||
|
from,
|
||||||
|
gas,
|
||||||
|
gasPrice,
|
||||||
|
selectedToken,
|
||||||
|
to,
|
||||||
|
unapprovedTxs,
|
||||||
|
}) {
|
||||||
|
const editingTx = {
|
||||||
|
...unapprovedTxs[editingTransactionId],
|
||||||
|
txParams: addHexPrefixToObjectValues({ from, gas, gasPrice }),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedToken) {
|
||||||
|
const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
||||||
|
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
||||||
|
x => ('00' + x.toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
|
||||||
|
Object.assign(editingTx.txParams, addHexPrefixToObjectValues({
|
||||||
|
value: '0',
|
||||||
|
to: selectedToken.address,
|
||||||
|
data,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
const { data } = unapprovedTxs[editingTransactionId].txParams
|
||||||
|
|
||||||
|
Object.assign(editingTx.txParams, addHexPrefixToObjectValues({
|
||||||
|
value: amount,
|
||||||
|
to,
|
||||||
|
data,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (typeof editingTx.txParams.data === 'undefined') {
|
||||||
|
delete editingTx.txParams.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressIsNew (toAccounts, newAddress) {
|
||||||
|
return !toAccounts.find(({ address }) => newAddress === address)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addressIsNew,
|
||||||
|
formShouldBeDisabled,
|
||||||
|
constructTxParams,
|
||||||
|
constructUpdatedTx,
|
||||||
|
}
|
33
ui/app/components/send_/send.constants.js
Normal file
33
ui/app/components/send_/send.constants.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
|
||||||
|
|
||||||
|
const MIN_GAS_PRICE_HEX = (100000000).toString(16)
|
||||||
|
const MIN_GAS_PRICE_DEC = '100000000'
|
||||||
|
const MIN_GAS_LIMIT_DEC = '21000'
|
||||||
|
const MIN_GAS_LIMIT_HEX = (parseInt(MIN_GAS_LIMIT_DEC)).toString(16)
|
||||||
|
|
||||||
|
const MIN_GAS_PRICE_GWEI = ethUtil.addHexPrefix(conversionUtil(MIN_GAS_PRICE_HEX, {
|
||||||
|
fromDenomination: 'WEI',
|
||||||
|
toDenomination: 'GWEI',
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
numberOfDecimals: 1,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
multiplicandBase: 16,
|
||||||
|
multiplierBase: 16,
|
||||||
|
})
|
||||||
|
|
||||||
|
const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb'
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
MIN_GAS_PRICE_GWEI,
|
||||||
|
MIN_GAS_PRICE_HEX,
|
||||||
|
MIN_GAS_PRICE_DEC,
|
||||||
|
MIN_GAS_LIMIT_HEX,
|
||||||
|
MIN_GAS_LIMIT_DEC,
|
||||||
|
MIN_GAS_TOTAL,
|
||||||
|
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
|
||||||
|
}
|
@ -2,14 +2,14 @@ import { valuesFor } from '../../util'
|
|||||||
import abi from 'human-standard-token-abi'
|
import abi from 'human-standard-token-abi'
|
||||||
import {
|
import {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} from './conversion-util'
|
} from '../../conversion-util'
|
||||||
|
|
||||||
const selectors = {
|
const selectors = {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
autoAddToBetaUI,
|
autoAddToBetaUI,
|
||||||
getConversionRate,
|
|
||||||
getAddressBook,
|
getAddressBook,
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
|
getConvertedCurrency,
|
||||||
getCurrentAccountWithSendEtherInfo,
|
getCurrentAccountWithSendEtherInfo,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getCurrentNetwork,
|
getCurrentNetwork,
|
||||||
@ -17,6 +17,7 @@ const selectors = {
|
|||||||
getForceGasMin,
|
getForceGasMin,
|
||||||
getGasLimit,
|
getGasLimit,
|
||||||
getGasPrice,
|
getGasPrice,
|
||||||
|
getGasTotal,
|
||||||
getSelectedAccount,
|
getSelectedAccount,
|
||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
getSelectedIdentity,
|
getSelectedIdentity,
|
||||||
@ -25,12 +26,18 @@ const selectors = {
|
|||||||
getSelectedTokenExchangeRate,
|
getSelectedTokenExchangeRate,
|
||||||
getSelectedTokenToFiatRate,
|
getSelectedTokenToFiatRate,
|
||||||
getSendAmount,
|
getSendAmount,
|
||||||
|
getSendEditingTransactionId,
|
||||||
getSendErrors,
|
getSendErrors,
|
||||||
getSendFrom,
|
getSendFrom,
|
||||||
|
getSendFromObject,
|
||||||
getSendFromBalance,
|
getSendFromBalance,
|
||||||
getSendMaxModeState,
|
getSendMaxModeState,
|
||||||
getSendTo,
|
getSendTo,
|
||||||
|
getSendToAccounts,
|
||||||
|
getTokenBalance,
|
||||||
getTokenExchangeRate,
|
getTokenExchangeRate,
|
||||||
|
getUnapprovedTxs,
|
||||||
|
isSendFormInError,
|
||||||
transactionsSelector,
|
transactionsSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +91,18 @@ function getTokenExchangeRate (state, tokenSymbol) {
|
|||||||
return tokenExchangeRate
|
return tokenExchangeRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUnapprovedTxs (state) {
|
||||||
|
return state.metamask.unapprovedTxs
|
||||||
|
}
|
||||||
|
|
||||||
function getConversionRate (state) {
|
function getConversionRate (state) {
|
||||||
return state.metamask.conversionRate
|
return state.metamask.conversionRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getConvertedCurrency (state) {
|
||||||
|
return state.metamask.currentCurrency
|
||||||
|
}
|
||||||
|
|
||||||
function getAddressBook (state) {
|
function getAddressBook (state) {
|
||||||
return state.metamask.addressBook
|
return state.metamask.addressBook
|
||||||
}
|
}
|
||||||
@ -97,11 +112,13 @@ function accountsWithSendEtherInfoSelector (state) {
|
|||||||
accounts,
|
accounts,
|
||||||
identities,
|
identities,
|
||||||
} = state.metamask
|
} = state.metamask
|
||||||
|
console.log(`accountsWithSendEtherInfoSelector accounts`, accounts);
|
||||||
|
console.log(`accountsWithSendEtherInfoSelector identities`, identities);
|
||||||
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
||||||
return Object.assign({}, account, identities[key])
|
return Object.assign({}, account, identities[key])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log(`accountsWithSendEtherInfoSelector accountsWithSendEtherInfo`, accountsWithSendEtherInfo);
|
||||||
return accountsWithSendEtherInfo
|
return accountsWithSendEtherInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +149,10 @@ function getGasPrice (state) {
|
|||||||
return state.metamask.send.gasPrice
|
return state.metamask.send.gasPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGasTotal (state) {
|
||||||
|
return state.metamask.send.gasTotal
|
||||||
|
}
|
||||||
|
|
||||||
function getGasLimit (state) {
|
function getGasLimit (state) {
|
||||||
return state.metamask.send.gasLimit
|
return state.metamask.send.gasLimit
|
||||||
}
|
}
|
||||||
@ -144,8 +165,12 @@ function getSendFrom (state) {
|
|||||||
return state.metamask.send.from
|
return state.metamask.send.from
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSendFromObject (state) {
|
||||||
|
return getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
|
||||||
|
}
|
||||||
|
|
||||||
function getSendFromBalance (state) {
|
function getSendFromBalance (state) {
|
||||||
const from = state.metamask.send.from || {}
|
const from = getSendFrom(state) || getSelectedAccount(state)
|
||||||
return from.balance
|
return from.balance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +228,10 @@ function getCurrentViewContext (state) {
|
|||||||
return currentView.context
|
return currentView.context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSendEditingTransactionId (state) {
|
||||||
|
return state.metamask.send.editingTransactionId
|
||||||
|
}
|
||||||
|
|
||||||
function getSendErrors (state) {
|
function getSendErrors (state) {
|
||||||
return state.metamask.send.errors
|
return state.metamask.send.errors
|
||||||
}
|
}
|
||||||
@ -211,6 +240,10 @@ function getSendTo (state) {
|
|||||||
return state.metamask.send.to
|
return state.metamask.send.to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTokenBalance (state) {
|
||||||
|
return state.metamask.send.tokenBalance
|
||||||
|
}
|
||||||
|
|
||||||
function getSendToAccounts (state) {
|
function getSendToAccounts (state) {
|
||||||
const fromAccounts = accountsWithSendEtherInfoSelector(state)
|
const fromAccounts = accountsWithSendEtherInfoSelector(state)
|
||||||
const addressBookAccounts = getAddressBook(state)
|
const addressBookAccounts = getAddressBook(state)
|
||||||
@ -222,3 +255,8 @@ function getSendToAccounts (state) {
|
|||||||
function getCurrentNetwork (state) {
|
function getCurrentNetwork (state) {
|
||||||
return state.metamask.network
|
return state.metamask.network
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSendFormInError (state) {
|
||||||
|
const { amount, to } = getSendErrors(state)
|
||||||
|
return Boolean(amount || toError !== null)
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
const {
|
||||||
|
addCurrencies,
|
||||||
|
conversionUtil,
|
||||||
|
conversionGTE,
|
||||||
|
multiplyCurrencies,
|
||||||
|
} = require('../../conversion-util')
|
||||||
|
const {
|
||||||
|
calcTokenAmount,
|
||||||
|
} = require('../../token-util')
|
||||||
|
|
||||||
|
function isBalanceSufficient ({
|
||||||
|
amount = '0x0',
|
||||||
|
gasTotal = '0x0',
|
||||||
|
balance,
|
||||||
|
primaryCurrency,
|
||||||
|
amountConversionRate,
|
||||||
|
conversionRate,
|
||||||
|
}) {
|
||||||
|
const totalAmount = addCurrencies(amount, gasTotal, {
|
||||||
|
aBase: 16,
|
||||||
|
bBase: 16,
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
})
|
||||||
|
|
||||||
|
const balanceIsSufficient = conversionGTE(
|
||||||
|
{
|
||||||
|
value: balance,
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
fromCurrency: primaryCurrency,
|
||||||
|
conversionRate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: totalAmount,
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
conversionRate: amountConversionRate || conversionRate,
|
||||||
|
fromCurrency: primaryCurrency,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return balanceIsSufficient
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTokenBalanceSufficient ({
|
||||||
|
amount = '0x0',
|
||||||
|
tokenBalance,
|
||||||
|
decimals,
|
||||||
|
}) {
|
||||||
|
const amountInDec = conversionUtil(amount, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
})
|
||||||
|
|
||||||
|
const tokenBalanceIsSufficient = conversionGTE(
|
||||||
|
{
|
||||||
|
value: tokenBalance,
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: calcTokenAmount(amountInDec, decimals),
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return tokenBalanceIsSufficient
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGasTotal (gasLimit, gasPrice) {
|
||||||
|
return multiplyCurrencies(gasLimit, gasPrice, {
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
multiplicandBase: 16,
|
||||||
|
multiplierBase: 16,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getGasTotal,
|
||||||
|
isBalanceSufficient,
|
||||||
|
isTokenBalanceSufficient,
|
||||||
|
}
|
@ -10,10 +10,11 @@ const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN';
|
|||||||
const initState = {
|
const initState = {
|
||||||
fromDropdownOpen: false,
|
fromDropdownOpen: false,
|
||||||
toDropdownOpen: false,
|
toDropdownOpen: false,
|
||||||
|
errors: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
export default function reducer(state = initState, action = {}) {
|
export default function reducer({ send: sendState = initState }, action = {}) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case OPEN_FROM_DROPDOWN:
|
case OPEN_FROM_DROPDOWN:
|
||||||
return extend(sendState, {
|
return extend(sendState, {
|
||||||
|
@ -8,7 +8,7 @@ const reduceIdentities = require('./reducers/identities')
|
|||||||
const reduceMetamask = require('./reducers/metamask')
|
const reduceMetamask = require('./reducers/metamask')
|
||||||
const reduceApp = require('./reducers/app')
|
const reduceApp = require('./reducers/app')
|
||||||
const reduceLocale = require('./reducers/locale')
|
const reduceLocale = require('./reducers/locale')
|
||||||
const reduceSend = require('./ducks/send')
|
const reduceSend = require('./ducks/send').default
|
||||||
|
|
||||||
window.METAMASK_CACHED_LOG_STATE = null
|
window.METAMASK_CACHED_LOG_STATE = null
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ const { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE } = require('./routes')
|
|||||||
|
|
||||||
import PageContainer from './components/page-container/page-container.component'
|
import PageContainer from './components/page-container/page-container.component'
|
||||||
import SendHeader from './components/send_/send-header/send-header.container'
|
import SendHeader from './components/send_/send-header/send-header.container'
|
||||||
import PageContainerContent from './components/page-container/page-container-content.component'
|
import SendContent from './components/send_/send-content/send-content.component'
|
||||||
import PageContainerFooter from './components/page-container/page-container-footer.component'
|
import SendFooter from './components/send_/send-footer/send-footer.container'
|
||||||
|
|
||||||
SendTransactionScreen.contextTypes = {
|
SendTransactionScreen.contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
@ -57,8 +57,6 @@ function SendTransactionScreen () {
|
|||||||
gasLoadingError: false,
|
gasLoadingError: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleToChange = this.handleToChange.bind(this)
|
|
||||||
this.handleAmountChange = this.handleAmountChange.bind(this)
|
|
||||||
this.validateAmount = this.validateAmount.bind(this)
|
this.validateAmount = this.validateAmount.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,158 +174,6 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderHeader = function () {
|
|
||||||
const { selectedToken, clearSend, history } = this.props
|
|
||||||
|
|
||||||
return h('div.page-container__header', [
|
|
||||||
|
|
||||||
h('div.page-container__title', selectedToken ? this.context.t('sendTokens') : this.context.t('sendETH')),
|
|
||||||
|
|
||||||
h('div.page-container__subtitle', this.context.t('onlySendToEtherAddress')),
|
|
||||||
|
|
||||||
h('div.page-container__header-close', {
|
|
||||||
onClick: () => {
|
|
||||||
clearSend()
|
|
||||||
history.push(DEFAULT_ROUTE)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderErrorMessage = function (errorType) {
|
|
||||||
const { errors } = this.props
|
|
||||||
const errorMessage = errors[errorType]
|
|
||||||
|
|
||||||
return errorMessage
|
|
||||||
? h('div.send-v2__error', [ errorMessage ])
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.handleFromChange = async function (newFrom) {
|
|
||||||
const {
|
|
||||||
updateSendFrom,
|
|
||||||
tokenContract,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
if (tokenContract) {
|
|
||||||
const usersToken = await tokenContract.balanceOf(newFrom.address)
|
|
||||||
this.updateSendTokenBalance(usersToken)
|
|
||||||
}
|
|
||||||
updateSendFrom(newFrom)
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderFromRow = function () {
|
|
||||||
const {
|
|
||||||
from,
|
|
||||||
fromAccounts,
|
|
||||||
conversionRate,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const { fromDropdownOpen } = this.state
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
|
||||||
|
|
||||||
h('div.send-v2__form-label', 'From:'),
|
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
|
||||||
h(FromDropdown, {
|
|
||||||
dropdownOpen: fromDropdownOpen,
|
|
||||||
accounts: fromAccounts,
|
|
||||||
selectedAccount: from,
|
|
||||||
onSelect: newFrom => this.handleFromChange(newFrom),
|
|
||||||
openDropdown: () => this.setState({ fromDropdownOpen: true }),
|
|
||||||
closeDropdown: () => this.setState({ fromDropdownOpen: false }),
|
|
||||||
conversionRate,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.handleToChange = function (to, nickname = '') {
|
|
||||||
const {
|
|
||||||
updateSendTo,
|
|
||||||
updateSendErrors,
|
|
||||||
} = this.props
|
|
||||||
let toError = null
|
|
||||||
|
|
||||||
if (!to) {
|
|
||||||
toError = this.context.t('required')
|
|
||||||
} else if (!isValidAddress(to)) {
|
|
||||||
toError = this.context.t('invalidAddressRecipient')
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSendTo(to, nickname)
|
|
||||||
updateSendErrors({ to: toError })
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderToRow = function () {
|
|
||||||
const { toAccounts, errors, to, network } = this.props
|
|
||||||
|
|
||||||
const { toDropdownOpen } = this.state
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
|
||||||
|
|
||||||
h('div.send-v2__form-label', [
|
|
||||||
|
|
||||||
this.context.t('to'),
|
|
||||||
|
|
||||||
this.renderErrorMessage(this.context.t('to')),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
|
||||||
h(EnsInput, {
|
|
||||||
name: 'address',
|
|
||||||
placeholder: 'Recipient Address',
|
|
||||||
network,
|
|
||||||
to,
|
|
||||||
accounts: Object.entries(toAccounts).map(([key, account]) => account),
|
|
||||||
dropdownOpen: toDropdownOpen,
|
|
||||||
openDropdown: () => this.setState({ toDropdownOpen: true }),
|
|
||||||
closeDropdown: () => this.setState({ toDropdownOpen: false }),
|
|
||||||
onChange: this.handleToChange,
|
|
||||||
inError: Boolean(errors.to),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.handleAmountChange = function (value) {
|
|
||||||
const amount = value
|
|
||||||
const { updateSendAmount, setMaxModeTo } = this.props
|
|
||||||
|
|
||||||
setMaxModeTo(false)
|
|
||||||
this.validateAmount(amount)
|
|
||||||
updateSendAmount(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.setAmountToMax = function () {
|
|
||||||
const {
|
|
||||||
from: { balance },
|
|
||||||
updateSendAmount,
|
|
||||||
updateSendErrors,
|
|
||||||
tokenBalance,
|
|
||||||
selectedToken,
|
|
||||||
gasTotal,
|
|
||||||
} = this.props
|
|
||||||
const { decimals } = selectedToken || {}
|
|
||||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
|
||||||
|
|
||||||
const maxAmount = selectedToken
|
|
||||||
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
|
|
||||||
: subtractCurrencies(
|
|
||||||
ethUtil.addHexPrefix(balance),
|
|
||||||
ethUtil.addHexPrefix(gasTotal),
|
|
||||||
{ toNumericBase: 'hex' }
|
|
||||||
)
|
|
||||||
|
|
||||||
updateSendErrors({ amount: null })
|
|
||||||
|
|
||||||
updateSendAmount(maxAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.validateAmount = function (value) {
|
SendTransactionScreen.prototype.validateAmount = function (value) {
|
||||||
const {
|
const {
|
||||||
@ -384,254 +230,19 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
|
|||||||
updateSendErrors({ amount: amountError })
|
updateSendErrors({ amount: amountError })
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderAmountRow = function () {
|
|
||||||
const {
|
|
||||||
selectedToken,
|
|
||||||
primaryCurrency = 'ETH',
|
|
||||||
convertedCurrency,
|
|
||||||
amountConversionRate,
|
|
||||||
errors,
|
|
||||||
amount,
|
|
||||||
setMaxModeTo,
|
|
||||||
maxModeOn,
|
|
||||||
gasTotal,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
|
||||||
|
|
||||||
h('div.send-v2__form-label', [
|
|
||||||
'Amount:',
|
|
||||||
this.renderErrorMessage('amount'),
|
|
||||||
!errors.amount && gasTotal && h('div.send-v2__amount-max', {
|
|
||||||
onClick: (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
setMaxModeTo(true)
|
|
||||||
this.setAmountToMax()
|
|
||||||
},
|
|
||||||
}, [ !maxModeOn ? this.context.t('max') : '' ]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
|
||||||
h(CurrencyDisplay, {
|
|
||||||
inError: Boolean(errors.amount),
|
|
||||||
primaryCurrency,
|
|
||||||
convertedCurrency,
|
|
||||||
selectedToken,
|
|
||||||
value: amount || '0x0',
|
|
||||||
conversionRate: amountConversionRate,
|
|
||||||
handleChange: this.handleAmountChange,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderGasRow = function () {
|
|
||||||
const {
|
|
||||||
conversionRate,
|
|
||||||
convertedCurrency,
|
|
||||||
showCustomizeGasModal,
|
|
||||||
gasTotal,
|
|
||||||
} = this.props
|
|
||||||
const { gasLoadingError } = this.state
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
|
||||||
|
|
||||||
h('div.send-v2__form-label', this.context.t('gasFee')),
|
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
|
||||||
|
|
||||||
h(GasFeeDisplay, {
|
|
||||||
gasTotal,
|
|
||||||
conversionRate,
|
|
||||||
convertedCurrency,
|
|
||||||
onClick: showCustomizeGasModal,
|
|
||||||
gasLoadingError,
|
|
||||||
}),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderMemoRow = function () {
|
|
||||||
const { updateSendMemo, memo } = this.props
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
|
||||||
|
|
||||||
h('div.send-v2__form-label', 'Transaction Memo:'),
|
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
|
||||||
h(MemoTextArea, {
|
|
||||||
memo,
|
|
||||||
onChange: (event) => updateSendMemo(event.target.value),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderForm = function () {
|
|
||||||
return h(PageContainerContent, [
|
|
||||||
h('.send-v2__form', [
|
|
||||||
this.renderFromRow(),
|
|
||||||
|
|
||||||
this.renderToRow(),
|
|
||||||
|
|
||||||
this.renderAmountRow(),
|
|
||||||
|
|
||||||
this.renderGasRow(),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderFooter = function () {
|
|
||||||
const {
|
|
||||||
clearSend,
|
|
||||||
gasTotal,
|
|
||||||
tokenBalance,
|
|
||||||
selectedToken,
|
|
||||||
errors: { amount: amountError, to: toError },
|
|
||||||
history,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const missingTokenBalance = selectedToken && !tokenBalance
|
|
||||||
const noErrors = !amountError && toError === null
|
|
||||||
|
|
||||||
return h(PageContainerFooter, {
|
|
||||||
onCancel: () => {
|
|
||||||
clearSend()
|
|
||||||
history.push(DEFAULT_ROUTE)
|
|
||||||
},
|
|
||||||
onSubmit: e => this.onSubmit(e),
|
|
||||||
disabled: !noErrors || !gasTotal || missingTokenBalance,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.render = function () {
|
SendTransactionScreen.prototype.render = function () {
|
||||||
|
const { history } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h(PageContainer, [
|
h(PageContainer, [
|
||||||
|
|
||||||
h(SendHeader),
|
h(SendHeader),
|
||||||
|
|
||||||
this.renderForm(),
|
h(SendContent),
|
||||||
|
|
||||||
this.renderFooter(),
|
h(SendFooter, { history }),
|
||||||
])
|
])
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.addToAddressBookIfNew = function (newAddress, nickname = '') {
|
|
||||||
const { toAccounts, addToAddressBook } = this.props
|
|
||||||
if (!toAccounts.find(({ address }) => newAddress === address)) {
|
|
||||||
// TODO: nickname, i.e. addToAddressBook(recipient, nickname)
|
|
||||||
addToAddressBook(newAddress, nickname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.getEditedTx = function () {
|
|
||||||
const {
|
|
||||||
from: {address: from},
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
gasLimit: gas,
|
|
||||||
gasPrice,
|
|
||||||
selectedToken,
|
|
||||||
editingTransactionId,
|
|
||||||
unapprovedTxs,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const editingTx = {
|
|
||||||
...unapprovedTxs[editingTransactionId],
|
|
||||||
txParams: {
|
|
||||||
from: ethUtil.addHexPrefix(from),
|
|
||||||
gas: ethUtil.addHexPrefix(gas),
|
|
||||||
gasPrice: ethUtil.addHexPrefix(gasPrice),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedToken) {
|
|
||||||
const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
|
||||||
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
|
||||||
x => ('00' + x.toString(16)).slice(-2)
|
|
||||||
).join('')
|
|
||||||
|
|
||||||
Object.assign(editingTx.txParams, {
|
|
||||||
value: ethUtil.addHexPrefix('0'),
|
|
||||||
to: ethUtil.addHexPrefix(selectedToken.address),
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const { data } = unapprovedTxs[editingTransactionId].txParams
|
|
||||||
|
|
||||||
Object.assign(editingTx.txParams, {
|
|
||||||
value: ethUtil.addHexPrefix(amount),
|
|
||||||
to: ethUtil.addHexPrefix(to),
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (typeof editingTx.txParams.data === 'undefined') {
|
|
||||||
delete editingTx.txParams.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return editingTx
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.onSubmit = function (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
const {
|
|
||||||
from: {address: from},
|
|
||||||
to: _to,
|
|
||||||
amount,
|
|
||||||
gasLimit: gas,
|
|
||||||
gasPrice,
|
|
||||||
signTokenTx,
|
|
||||||
signTx,
|
|
||||||
updateTx,
|
|
||||||
selectedToken,
|
|
||||||
editingTransactionId,
|
|
||||||
toNickname,
|
|
||||||
errors: { amount: amountError, to: toError },
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const noErrors = !amountError && toError === null
|
|
||||||
|
|
||||||
if (!noErrors) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const to = ethUtil.addHexPrefix(_to)
|
|
||||||
|
|
||||||
this.addToAddressBookIfNew(to, toNickname)
|
|
||||||
|
|
||||||
if (editingTransactionId) {
|
|
||||||
const editedTx = this.getEditedTx()
|
|
||||||
updateTx(editedTx)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
const txParams = {
|
|
||||||
from,
|
|
||||||
value: '0',
|
|
||||||
gas,
|
|
||||||
gasPrice,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedToken) {
|
|
||||||
txParams.value = amount
|
|
||||||
txParams.to = to
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(txParams).forEach(key => {
|
|
||||||
txParams[key] = ethUtil.addHexPrefix(txParams[key])
|
|
||||||
})
|
|
||||||
|
|
||||||
selectedToken
|
|
||||||
? signTokenTx(selectedToken.address, to, amount, txParams)
|
|
||||||
: signTx(txParams)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.history.push(CONFIRM_TRANSACTION_ROUTE)
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user