// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause
import React, { Component } from 'react';
import BigNumber from 'bignumber.js';
import { Clickable, Form as FetherForm, Header } from 'fether-ui';
import createDecorator from 'final-form-calculate';
import { chainId$, withoutLoading } from '@parity/light.js';
import debounce from 'debounce-promise';
import { Field, Form } from 'react-final-form';
import { fromWei, toWei } from '@parity/api/lib/util/wei';
import { inject, observer } from 'mobx-react';
import { isAddress } from '@parity/api/lib/util/address';
import light from '@parity/light.js-react';
import { Link } from 'react-router-dom';
import { OnChange } from 'react-final-form-listeners';
import { withProps } from 'recompose';
import { estimateGas } from '../../utils/transaction';
import RequireHealth from '../../RequireHealthOverlay';
import TokenBalance from '../../Tokens/TokensList/TokenBalance';
import TxDetails from './TxDetails';
import withAccount from '../../utils/withAccount';
import withBalance, { withEthBalance } from '../../utils/withBalance';
import withTokens from '../../utils/withTokens';
const DEFAULT_AMOUNT_MAX_CHARS = 9;
const MEDIUM_AMOUNT_MAX_CHARS = 14;
const MAX_GAS_PRICE = 40; // In Gwei
const MIN_GAS_PRICE = 3; // Safelow gas price from GasStation, in Gwei
@inject('parityStore', 'sendStore')
@withTokens
@withProps(({ match: { params: { tokenAddress } }, tokens }) => ({
token: tokens[tokenAddress]
}))
@withAccount
@light({
chainId: () => chainId$().pipe(withoutLoading())
})
@withBalance // Balance of current token (can be ETH)
@withEthBalance // ETH balance
@observer
class TxForm extends Component {
state = {
maxSelected: false,
showDetails: false
};
decorator = createDecorator({
field: /to|amount/, // when the value of these fields change...
updates: {
// ...set field "gas"
gas: async (value, allValues) => {
const { parityStore, token } = this.props;
let newGasEstimate = null;
if (this.preValidate(allValues) === true) {
try {
newGasEstimate = await estimateGas(
allValues,
token,
parityStore.api
);
} catch (error) {
console.error(error);
throw new Error('Unable to estimate gas');
}
}
return newGasEstimate;
}
}
});
changeAmountFontSize = amount => {
const amountLen = amount.toString().length;
if (amountLen > MEDIUM_AMOUNT_MAX_CHARS) {
return '-resize-font-small'; // Resize to fit an amount as small as one Wei
} else if (
MEDIUM_AMOUNT_MAX_CHARS >= amountLen &&
amountLen > DEFAULT_AMOUNT_MAX_CHARS
) {
return '-resize-font-medium';
}
return '-resize-font-default';
};
calculateMax = (gas, gasPrice) => {
const { token, balance } = this.props;
const gasBn = gas ? new BigNumber(gas) : new BigNumber(21000);
const gasPriceBn = new BigNumber(gasPrice);
let output;
if (token.address === 'ETH') {
output = fromWei(
toWei(balance).minus(gasBn.multipliedBy(toWei(gasPriceBn, 'shannon')))
);
output = output.isNegative() ? new BigNumber(0) : output;
} else {
output = balance;
}
return output;
};
estimatedTxFee = values => {
if (
!values.amount ||
!values.gas ||
!values.gasPrice ||
isNaN(values.amount) ||
isNaN(values.gas) ||
isNaN(values.gasPrice)
) {
return null;
}
return values.gas.multipliedBy(toWei(values.gasPrice, 'shannon'));
};
handleSubmit = values => {
const {
account: { address, type, transactionCount },
chainId,
history,
sendStore,
token
} = this.props;
sendStore.setTx({ ...values, chainId, token, transactionCount });
if (type === 'signer') {
history.push(`/send/${token.address}/from/${address}/txqrcode`);
} else {
history.push(`/send/${token.address}/from/${address}/unlock`);
}
};
recalculateMax = (args, state, { changeValue }) => {
changeValue(state, 'amount', value => {
return this.calculateMax(
state.formState.values.gas,
state.formState.values.gasPrice
);
});
};
toggleMax = () => {
this.setState({ maxSelected: !this.state.maxSelected });
};
showDetailsAnchor = () => {
return (