diff --git a/packages/light-react/src/Send/TxForm/TxForm.js b/packages/light-react/src/Send/TxForm/TxForm.js index 932dfb3605f51e1e408d5f665db567ff4c41e70b..3c4d90b2d946ae19f19c3399083f9f468e3777d1 100644 --- a/packages/light-react/src/Send/TxForm/TxForm.js +++ b/packages/light-react/src/Send/TxForm/TxForm.js @@ -5,21 +5,40 @@ import React, { Component } from 'react'; import { FormField, Header } from 'light-ui'; +import { fromWei, toWei } from '@parity/api/lib/util/wei'; import { inject, observer } from 'mobx-react'; import { Link } from 'react-router-dom'; import TokenBalance from '../../Tokens/TokensList/TokenBalance'; +import withBalance from '../../utils/withBalance'; const MAX_GAS_PRICE = 40; // In Gwei const MIN_GAS_PRICE = 3; // Safelow gas price from GasStation, in Gwei @inject('sendStore') +@withBalance(({ sendStore: { token } }) => token) @observer class Send extends Component { componentDidMount () { this.props.sendStore.estimateGas(); } + getMaxAmount = () => { + const { + balance, + sendStore: { estimated, tx } + } = this.props; + + // TODO this in sendStore as @computed? + return balance && estimated + ? +fromWei( + toWei(balance).minus( + estimated.multipliedBy(toWei(tx.gasPrice, 'shannon')) + ) + ) + : 0.01; + }; + handleChangeAmount = ({ target: { value } }) => this.props.sendStore.setTxAmount(value); @@ -35,6 +54,8 @@ class Send extends Component { } }; + handleMax = () => this.props.sendStore.setTxAmount(this.getMaxAmount()); + handleSubmit = e => { e.preventDefault(); const { history, sendStore } = this.props; @@ -65,6 +86,7 @@ class Send extends Component {
@@ -94,12 +124,14 @@ class Send extends Component { } diff --git a/packages/light-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js b/packages/light-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js index 4d4406c149adbcbafee1de5fd71a53e3f1162084..8b231f415390020cd2234b254ac2d72a8d012746 100644 --- a/packages/light-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js +++ b/packages/light-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js @@ -4,42 +4,14 @@ // SPDX-License-Identifier: MIT import React, { Component } from 'react'; -import abi from '@parity/shared/lib/contracts/abi/eip20'; -import { empty } from 'rxjs'; -import { - defaultAccount$, - isNullOrLoading, - makeContract$, - myBalance$ -} from '@parity/light.js'; -import { filter, map, switchMap } from 'rxjs/operators'; -import { fromWei } from '@parity/api/lib/util/wei'; import { inject } from 'mobx-react'; -import light from 'light-hoc'; import PropTypes from 'prop-types'; import { TokenCard } from 'light-ui'; import { withRouter } from 'react-router-dom'; -@light({ - balance: ({ token: { address, decimals } }) => { - if (!address) { - return empty(); - } - return address === 'ETH' - ? myBalance$().pipe( - map(value => (isNullOrLoading(value) ? null : value)), - map(value => value && +fromWei(value)) - ) - : defaultAccount$().pipe( - filter(x => x), - switchMap(defaultAccount => - makeContract$(address, abi).balanceOf$(defaultAccount) - ), - map(value => (isNullOrLoading(value) ? null : value)), - map(value => value && +value.div(10 ** decimals)) - ); - } -}) +import withBalance from '../../../utils/withBalance'; + +@withBalance() @inject('sendStore') @withRouter class TokenBalance extends Component { diff --git a/packages/light-react/src/stores/sendStore.js b/packages/light-react/src/stores/sendStore.js index 5a1ac63c279d3a75294066c799c486cb506eaa8e..fea2d9938520af8d9b85ff148b2dfa73cd80ebc5 100644 --- a/packages/light-react/src/stores/sendStore.js +++ b/packages/light-react/src/stores/sendStore.js @@ -14,7 +14,7 @@ import { toWei } from '@parity/api/lib/util/wei'; import parityStore from './parityStore'; import tokensStore from './tokensStore'; -const DEFAULT_GAS = 21000; // Default gas amount to show +const DEFAULT_GAS = new BigNumber(21000); // Default gas amount to show class SendStore { @observable blockNumber; // Current block number, used to calculate tx confirmations. @@ -22,9 +22,9 @@ class SendStore { @observable tokenAddress; // 'ETH', or the token contract address @observable tx = { - amount: 0.01, // In Ether or in token + amount: '', // In Ether or in token gasPrice: 4, // in Gwei - to: '0x00Ae02834e91810B223E54ce3f9B7875258a1747' + to: '' }; // The actual tx we are sending. No need to be observable. @observable txStatus; // Status of the tx, see wiki for details. @@ -106,6 +106,7 @@ class SendStore { if ( !this.tx || // There should be a tx !isAddress(this.tx.to) || // The address should be okay + !this.tx.amount || isNaN(this.tx.amount) || isNaN(this.tx.gasPrice) ) { @@ -161,6 +162,10 @@ class SendStore { */ @computed get txForEth () { + if (!this.isTxValid) { + return {}; + } + return { gasPrice: toWei(this.tx.gasPrice, 'shannon'), // shannon == gwei to: this.tx.to, @@ -174,6 +179,10 @@ class SendStore { */ @computed get txForErc20 () { + if (!this.isTxValid) { + return {}; + } + return { args: [ this.tx.to, @@ -222,7 +231,7 @@ class SendStore { // Since estimateGas is not always accurate, we add a 120% factor for buffer. const GAS_MULT_FACTOR = 1.2; - this.estimated = +estimated * GAS_MULT_FACTOR; // Don't store as BigNumber + this.estimated = estimated.multipliedBy(GAS_MULT_FACTOR); }; @action diff --git a/packages/light-react/src/stores/tokensStore.js b/packages/light-react/src/stores/tokensStore.js index 4e3fd24212fa5e56a12c9ab504381285a3ce63de..2c55ecffe59a5d88ed2fb1cc9d9f92754142c039 100644 --- a/packages/light-react/src/stores/tokensStore.js +++ b/packages/light-react/src/stores/tokensStore.js @@ -52,6 +52,7 @@ class TokensStore { this.tokens = { ETH: { address: 'ETH', + decimals: 18, logo: ethereumIcon, name: 'Ethereum', symbol: 'ETH' diff --git a/packages/light-react/src/utils/withBalance.js b/packages/light-react/src/utils/withBalance.js new file mode 100644 index 0000000000000000000000000000000000000000..48454daed661d996872739a0a711f24ae3ff311c --- /dev/null +++ b/packages/light-react/src/utils/withBalance.js @@ -0,0 +1,50 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +import abi from '@parity/shared/lib/contracts/abi/eip20'; +import { empty } from 'rxjs'; +import { + defaultAccount$, + isNullOrLoading, + makeContract$, + myBalance$ +} from '@parity/light.js'; +import { filter, map, switchMap } from 'rxjs/operators'; +import { fromWei } from '@parity/api/lib/util/wei'; +import light from 'light-hoc'; + +/** + * A HOC on light.js to get the current balance. + * + * @example + * @withBalance + * class MyComponent extends React.Component{ + * + * } + */ +export default (propsSelector = ({ token }) => token) => + light({ + balance: ownProps => { + // Find our token object in the props + const token = propsSelector(ownProps); + + if (!token.address) { + return empty(); + } + return token.address === 'ETH' + ? myBalance$().pipe( + map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null + map(value => value && fromWei(value)) + ) + : defaultAccount$().pipe( + filter(x => x), + switchMap(defaultAccount => + makeContract$(token.address, abi).balanceOf$(defaultAccount) + ), + map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null + map(value => value && value.div(10 ** token.decimals)) + ); + } + }); diff --git a/packages/light-ui/src/TokenCard/TokenCard.js b/packages/light-ui/src/TokenCard/TokenCard.js index 1af15ee5d15f904d52dd8aa2be8001309923e2e0..e1383899c0fb1bc10f5282d2e431a661ce3d3c95 100644 --- a/packages/light-ui/src/TokenCard/TokenCard.js +++ b/packages/light-ui/src/TokenCard/TokenCard.js @@ -30,7 +30,7 @@ const TokenCard = ({ {token.name ? token.name : }
- {Number.isFinite(balance) ? ( + {balance ? ( {balance.toFixed(decimals)} ) : showBalance ? ( @@ -48,7 +48,6 @@ TokenCard.defaultProps = { }; TokenCard.propTypes = { - balance: PropTypes.number, decimals: PropTypes.number.isRequired, token: PropTypes.shape({ logo: PropTypes.string,