Stricter balance checks during swap (#793)

* check currentBalance before showing Amount Field and GenerateTx button

* add token balance check

* check wallet balance

* comments

* simplify wallet balance check
This commit is contained in:
Eddie Wang 2018-01-15 01:51:35 -05:00 committed by Daniel Ternyak
parent b8b0cdece3
commit eb4fd1cce8
5 changed files with 80 additions and 32 deletions

View File

@ -4,7 +4,7 @@ import EthTx from 'ethereumjs-tx';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { getTransaction, isNetworkRequestPending } from 'selectors/transaction'; import { getTransaction, isNetworkRequestPending, isValidAmount } from 'selectors/transaction';
import { getWalletType } from 'selectors/wallet'; import { getWalletType } from 'selectors/wallet';
interface StateProps { interface StateProps {
@ -12,17 +12,24 @@ interface StateProps {
networkRequestPending: boolean; networkRequestPending: boolean;
isFullTransaction: boolean; isFullTransaction: boolean;
isWeb3Wallet: boolean; isWeb3Wallet: boolean;
validAmount: boolean;
} }
class GenerateTransactionClass extends Component<StateProps> { class GenerateTransactionClass extends Component<StateProps> {
public render() { public render() {
const { isFullTransaction, isWeb3Wallet, transaction, networkRequestPending } = this.props; const {
isFullTransaction,
isWeb3Wallet,
transaction,
networkRequestPending,
validAmount
} = this.props;
return ( return (
<WithSigner <WithSigner
isWeb3={isWeb3Wallet} isWeb3={isWeb3Wallet}
withSigner={signer => ( withSigner={signer => (
<button <button
disabled={!isFullTransaction || networkRequestPending} disabled={!isFullTransaction || networkRequestPending || !validAmount}
className="btn btn-info btn-block" className="btn btn-info btn-block"
onClick={signer(transaction)} onClick={signer(transaction)}
> >
@ -37,5 +44,6 @@ class GenerateTransactionClass extends Component<StateProps> {
export const GenerateTransaction = connect((state: AppState) => ({ export const GenerateTransaction = connect((state: AppState) => ({
...getTransaction(state), ...getTransaction(state),
networkRequestPending: isNetworkRequestPending(state), networkRequestPending: isNetworkRequestPending(state),
isWeb3Wallet: getWalletType(state).isWeb3Wallet isWeb3Wallet: getWalletType(state).isWeb3Wallet,
validAmount: isValidAmount(state)
}))(GenerateTransactionClass); }))(GenerateTransactionClass);

View File

@ -7,15 +7,20 @@ import { GenerateTransaction, SendButton, SigningStatus, GasSlider } from 'compo
import { resetWallet, TResetWallet } from 'actions/wallet'; import { resetWallet, TResetWallet } from 'actions/wallet';
import translate from 'translations'; import translate from 'translations';
import { getUnit } from 'selectors/transaction'; import { getUnit } from 'selectors/transaction';
import { getCurrentBalance } from 'selectors/wallet';
import Spinner from 'components/ui/Spinner';
import { Wei, TokenValue } from 'libs/units';
interface StateProps { interface StateProps {
unit: string; unit: string;
resetWallet: TResetWallet; resetWallet: TResetWallet;
currentBalance: Wei | TokenValue | null;
} }
type Props = StateProps; type Props = StateProps;
class FieldsClass extends Component<Props> { class FieldsClass extends Component<Props> {
public render() { public render() {
const { currentBalance } = this.props;
return ( return (
<div className="Tab-content-pane"> <div className="Tab-content-pane">
<div className="row form-group"> <div className="row form-group">
@ -41,7 +46,11 @@ class FieldsClass extends Component<Props> {
<div className="row form-group"> <div className="row form-group">
<div className="col-xs-12"> <div className="col-xs-12">
<label>{translate('SEND_amount')}</label> <label>{translate('SEND_amount')}</label>
{currentBalance === null ? (
<div className="row text-center">
<Spinner />
</div>
) : (
<AmountFieldFactory <AmountFieldFactory
withProps={({ currentValue, isValid }) => ( withProps={({ currentValue, isValid }) => (
<React.Fragment> <React.Fragment>
@ -62,6 +71,7 @@ class FieldsClass extends Component<Props> {
</React.Fragment> </React.Fragment>
)} )}
/> />
)}
</div> </div>
</div> </div>
<div className="row form-group"> <div className="row form-group">
@ -72,7 +82,13 @@ class FieldsClass extends Component<Props> {
<SigningStatus /> <SigningStatus />
<div className="row form-group"> <div className="row form-group">
<div className="col-xs-12 clearfix"> <div className="col-xs-12 clearfix">
{currentBalance === null ? (
<div className="row text-center">
<Spinner />
</div>
) : (
<GenerateTransaction /> <GenerateTransaction />
)}
</div> </div>
</div> </div>
<div className="row form-group"> <div className="row form-group">
@ -86,6 +102,7 @@ class FieldsClass extends Component<Props> {
}; };
} }
export const Fields = connect((state: AppState) => ({ unit: getUnit(state) }), { resetWallet })( export const Fields = connect(
FieldsClass (state: AppState) => ({ unit: getUnit(state), currentBalance: getCurrentBalance(state) }),
); { resetWallet }
)(FieldsClass);

View File

@ -1,6 +1,6 @@
import { Wei } from 'libs/units'; import { Wei } from 'libs/units';
export interface Balance { export interface Balance {
wei: Wei; wei: Wei | null;
isPending: boolean; isPending: boolean;
} }

View File

@ -15,7 +15,7 @@ export interface State {
inst?: IWallet | null; inst?: IWallet | null;
config?: WalletConfig | null; config?: WalletConfig | null;
// in ETH // in ETH
balance: Balance | { wei: null }; balance: Balance;
tokens: { tokens: {
[key: string]: { [key: string]: {
balance: TokenValue; balance: TokenValue;

View File

@ -3,6 +3,7 @@ import { getUnit, getTokenTo, getTokenValue } from './meta';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units'; import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units';
import { getDataExists, getValidGasCost } from 'selectors/transaction'; import { getDataExists, getValidGasCost } from 'selectors/transaction';
import { getCurrentBalance } from 'selectors/wallet';
interface ICurrentValue { interface ICurrentValue {
raw: string; raw: string;
@ -38,18 +39,40 @@ const isValidCurrentTo = (state: AppState) => {
} }
}; };
const isValidAmount = (state: AppState) => { const isValidAmount = (state: AppState): boolean => {
const currentValue = getCurrentValue(state); const currentValue = getCurrentValue(state);
const dataExists = getDataExists(state); const dataExists = getDataExists(state);
const validGasCost = getValidGasCost(state); const validGasCost = getValidGasCost(state);
// We do some wallet validation here.
// For some reason with MetaMask, sometimes the currentValue.value is not a null
// but instead a BN with a value equal to currentValue.raw - even though the wallet
// doesn't have enough of a balance.
// Get the wallet balance (token value or ether value)
const walletBalance = getCurrentBalance(state);
// We ensure that we have a valid walletBalance (token or Ether is fine)
if (!walletBalance) {
return false;
}
if (isEtherTransaction(state)) { if (isEtherTransaction(state)) {
// if data exists with no value, just check if gas is enough // if data exists with no value, just check if gas is enough
if (dataExists && !currentValue.value && currentValue.raw === '') { if (dataExists && !currentValue.value && currentValue.raw === '') {
return validGasCost; return validGasCost;
} }
// if the currentValue.value is not null, then compare it against the walletBalance.
if (currentValue.value) {
return walletBalance.cmp(currentValue.value) > 0;
}
return !!currentValue.value; return !!currentValue.value;
} else { } else {
// if the currentValue.value is not null, then compare it against the walletBalance.
if (currentValue.value) {
return walletBalance.cmp(currentValue.value) > 0;
}
return !!currentValue.value; return !!currentValue.value;
} }
}; };