|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/* @flow */
|
|
|
|
|
|
|
|
|
|
import TrezorConnect from 'trezor-connect';
|
|
|
|
|
import BigNumber from 'bignumber.js';
|
|
|
|
|
import * as SEND from 'actions/constants/send';
|
|
|
|
|
import { findDevice, getPendingAmount } from 'reducers/utils';
|
|
|
|
@ -9,6 +9,7 @@ import type {
|
|
|
|
|
Dispatch,
|
|
|
|
|
GetState,
|
|
|
|
|
PayloadAction,
|
|
|
|
|
PromiseAction,
|
|
|
|
|
BlockchainFeeLevel,
|
|
|
|
|
} from 'flowtype';
|
|
|
|
|
import type { State, FeeLevel } from 'reducers/SendFormRippleReducer';
|
|
|
|
@ -59,7 +60,7 @@ export const onFeeUpdated = (network: string, feeLevels: Array<BlockchainFeeLeve
|
|
|
|
|
/*
|
|
|
|
|
* Recalculate amount, total and fees
|
|
|
|
|
*/
|
|
|
|
|
export const validation = (): PayloadAction<State> => (dispatch: Dispatch, getState: GetState): State => {
|
|
|
|
|
export const validation = (prevState: State): PayloadAction<State> => (dispatch: Dispatch, getState: GetState): State => {
|
|
|
|
|
// clone deep nested object
|
|
|
|
|
// to avoid overrides across state history
|
|
|
|
|
let state: State = JSON.parse(JSON.stringify(getState().sendFormRipple));
|
|
|
|
@ -74,6 +75,9 @@ export const validation = (): PayloadAction<State> => (dispatch: Dispatch, getSt
|
|
|
|
|
state = dispatch(amountValidation(state));
|
|
|
|
|
state = dispatch(feeValidation(state));
|
|
|
|
|
state = dispatch(destinationTagValidation(state));
|
|
|
|
|
if (state.touched.address && prevState.address !== state.address) {
|
|
|
|
|
dispatch(addressBalanceValidation(state));
|
|
|
|
|
}
|
|
|
|
|
return state;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -135,6 +139,43 @@ const addressValidation = ($state: State): PayloadAction<State> => (dispatch: Di
|
|
|
|
|
return state;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Address balance validation
|
|
|
|
|
* Fetch data from trezor-connect and set minimum required amount in reducer
|
|
|
|
|
*/
|
|
|
|
|
const addressBalanceValidation = ($state: State): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
|
|
|
const { network } = getState().selectedAccount;
|
|
|
|
|
if (!network) return;
|
|
|
|
|
|
|
|
|
|
let minAmount: string = '0';
|
|
|
|
|
const response = await TrezorConnect.rippleGetAccountInfo({
|
|
|
|
|
account: {
|
|
|
|
|
descriptor: $state.address,
|
|
|
|
|
},
|
|
|
|
|
coin: network.shortcut,
|
|
|
|
|
});
|
|
|
|
|
if (response.success) {
|
|
|
|
|
const empty = response.payload.sequence <= 0 && response.payload.balance === '0';
|
|
|
|
|
if (empty) {
|
|
|
|
|
minAmount = toDecimalAmount(response.payload.reserve, network.decimals);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: consider checking local (known) accounts reserve instead of async fetching
|
|
|
|
|
|
|
|
|
|
// a2 (not empty): rJX2KwzaLJDyFhhtXKi3htaLfaUH2tptEX
|
|
|
|
|
// a4 (empty): r9skfe7kZkvqss7oMB3tuj4a59PXD5wRa2
|
|
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
|
type: SEND.CHANGE,
|
|
|
|
|
networkType: 'ripple',
|
|
|
|
|
state: {
|
|
|
|
|
...getState().sendFormRipple,
|
|
|
|
|
minAmount,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Address label assignation
|
|
|
|
|
*/
|
|
|
|
@ -193,13 +234,21 @@ const amountValidation = ($state: State): PayloadAction<State> => (dispatch: Dis
|
|
|
|
|
state.errors.amount = 'Amount is not a number';
|
|
|
|
|
} else {
|
|
|
|
|
const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol);
|
|
|
|
|
|
|
|
|
|
if (!state.amount.match(XRP_6_RE)) {
|
|
|
|
|
state.errors.amount = 'Maximum 6 decimals allowed';
|
|
|
|
|
} else if (new BigNumber(state.total).greaterThan(new BigNumber(account.balance).minus(pendingAmount))) {
|
|
|
|
|
state.errors.amount = 'Not enough funds';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state.errors.amount && new BigNumber(account.balance).minus(state.total).lt(account.reserve)) {
|
|
|
|
|
state.errors.amount = `Not enough funds. Reserved amount for this account is ${account.reserve} ${state.networkSymbol}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state.errors.amount && new BigNumber(state.amount).lt(state.minAmount)) {
|
|
|
|
|
state.errors.amount = `Amount is too low. Minimum amount for creating a new account is ${state.minAmount} ${state.networkSymbol}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return state;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|