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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ import { getUnit, getTokenTo, getTokenValue } from './meta';
import { AppState } from 'reducers';
import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units';
import { getDataExists, getValidGasCost } from 'selectors/transaction';
import { getCurrentBalance } from 'selectors/wallet';
interface ICurrentValue {
raw: string;
@ -38,18 +39,40 @@ const isValidCurrentTo = (state: AppState) => {
}
};
const isValidAmount = (state: AppState) => {
const isValidAmount = (state: AppState): boolean => {
const currentValue = getCurrentValue(state);
const dataExists = getDataExists(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 data exists with no value, just check if gas is enough
if (dataExists && !currentValue.value && currentValue.raw === '') {
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;
} 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;
}
};