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:
parent
b8b0cdece3
commit
eb4fd1cce8
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue