Newer
Older
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
import React, { Component } from "react";
import debounce from "debounce-promise";
import { Field, Form } from "react-final-form";
import { Form as FetherForm, Header } from "fether-ui";
import { inject, observer } from "mobx-react";
import { isAddress } from "@parity/api/lib/util/address";
import { Link } from "react-router-dom";
import { toWei } from "@parity/api/lib/util/wei";
import { withProps } from "recompose";
import { consumeTokens } from "../../contexts/TokensContext.js";
import { estimateGas } from "../../utils/estimateGas";
import TokenBalance from "../../Tokens/TokensList/TokenBalance";
import withAccount from "../../utils/withAccount.js";
import withBalance, { withEthBalance } from "../../utils/withBalance";
const MAX_GAS_PRICE = 40; // In Gwei
const MIN_GAS_PRICE = 3; // Safelow gas price from GasStation, in Gwei
@consumeTokens
@withProps(({ match: { params: { tokenAddress } }, tokens }) => ({
token: tokens[tokenAddress]
@withBalance // Balance of current token (can be ETH)
@withEthBalance // ETH balance
@observer
class Send extends Component {
const { accountAddress, history, sendStore, token } = this.props;
history.push(`/send/${token.address}/from/${accountAddress}/signer`);
<Link to={`/tokens/${accountAddress}`} className="icon -close">
title={token && <h1>Send {token.name}</h1>}
<div className="window_content">
<div className="box -padded">
initialValues={{ gasPrice: 4, ...tx }}
validate={this.validateForm}
render={({ handleSubmit, valid, validating, values }) => (
<form className="send-form" onSubmit={handleSubmit}>
<fieldset className="form_fields">
render={FetherForm.Field}
required
as="textarea"
className="-sm"
label="To"
name="to"
placeholder="0x..."
render={FetherForm.Field}
/>
<Field
centerText={`${values.gasPrice} GWEI`}
className="-range"
label="Transaction Fee"
leftText="Slow"
max={MAX_GAS_PRICE}
min={MIN_GAS_PRICE}
render={FetherForm.Slider}
required
<button
disabled={!valid || validating}
</button>
</nav>
</form>
)}
/>
onClick={null} // To disable cursor:pointer on card // TODO Can this be done better?
/**
* Estimate gas amount, and validate that the user has enough balance to make
* the tx.
*/
validateAmount = debounce(async values => {
const { balance, ethBalance, parityStore, token } = this.props;
const amount = +values.amount;
if (!amount || isNaN(amount)) {
return { amount: `You don't have enough ${token.symbol} balance` };
const estimated = await estimateGas(values, token, parityStore.api);
if (!ethBalance || isNaN(estimated)) {
throw new Error('No "ethBalance" or "estimated" value.');
// Verify that `gas + (eth amount if sending eth) <= ethBalance`
.mul(toWei(values.gasPrice, "shannon"))
.plus(token.address === "ETH" ? toWei(values.amount) : 0)
}
} catch (err) {
return {
};
}
}, 1000);
validateForm = values => {
return Object.keys(errors).length ? errors : this.validateAmount(values);