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 { 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);
|
||||
|
|
|
@ -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,27 +46,32 @@ class FieldsClass extends Component<Props> {
|
|||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<label>{translate('SEND_amount')}</label>
|
||||
|
||||
<AmountFieldFactory
|
||||
withProps={({ currentValue, isValid }) => (
|
||||
<React.Fragment>
|
||||
{!isValid && (
|
||||
<h5 style={{ color: 'red' }}>
|
||||
WARNING: Your ether or token balance is not high enough to complete this
|
||||
transaction! Please send more funds or switch to a different wallet
|
||||
</h5>
|
||||
)}
|
||||
{isValid && (
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
value={`${currentValue.raw} ${this.props.unit}`}
|
||||
readOnly={true}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
{currentBalance === null ? (
|
||||
<div className="row text-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<AmountFieldFactory
|
||||
withProps={({ currentValue, isValid }) => (
|
||||
<React.Fragment>
|
||||
{!isValid && (
|
||||
<h5 style={{ color: 'red' }}>
|
||||
WARNING: Your ether or token balance is not high enough to complete this
|
||||
transaction! Please send more funds or switch to a different wallet
|
||||
</h5>
|
||||
)}
|
||||
{isValid && (
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
value={`${currentValue.raw} ${this.props.unit}`}
|
||||
readOnly={true}
|
||||
/>
|
||||
)}
|
||||
</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">
|
||||
<GenerateTransaction />
|
||||
{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);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Wei } from 'libs/units';
|
||||
|
||||
export interface Balance {
|
||||
wei: Wei;
|
||||
wei: Wei | null;
|
||||
isPending: boolean;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue