openethereum/js/packages/dapp-localtx/Transaction/transaction.js

397 lines
8.7 KiB
JavaScript
Raw Normal View History

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-11-17 10:15:11 +01:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2016-11-17 11:01:21 +01:00
import BigNumber from 'bignumber.js';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
2016-11-17 10:15:11 +01:00
import classnames from 'classnames';
import { api } from '../parity';
import styles from './transaction.css';
import IdentityIcon from '../IdentityIcon';
2016-11-17 10:15:11 +01:00
class BaseTransaction extends Component {
shortHash (hash) {
2016-11-17 11:01:21 +01:00
return `${hash.substr(0, 5)}..${hash.substr(hash.length - 3)}`;
2016-11-17 10:15:11 +01:00
}
renderHash (hash) {
return (
<code title={ hash } className={ styles.txhash }>
{ hash }
2016-11-17 10:15:11 +01:00
</code>
);
}
renderFrom (transaction) {
if (!transaction) {
return '-';
}
return (
<div title={ transaction.from } className={ styles.from }>
<IdentityIcon
address={ transaction.from }
/>
2016-11-17 10:15:11 +01:00
</div>
);
}
renderGasPrice (transaction) {
if (!transaction) {
return '-';
}
return (
2016-11-17 14:00:53 +01:00
<span title={ `${transaction.gasPrice.toFormat(0)} wei` }>
2016-11-17 10:15:11 +01:00
{ api.util.fromWei(transaction.gasPrice, 'shannon').toFormat(2) }&nbsp;shannon
</span>
);
}
renderGas (transaction) {
if (!transaction) {
return '-';
}
return (
<span title={ `${transaction.gas.toFormat(0)} Gas` }>
2016-11-17 14:00:53 +01:00
{ transaction.gas.div(10 ** 6).toFormat(3) }&nbsp;MGas
2016-11-17 10:15:11 +01:00
</span>
);
}
renderPropagation (stats) {
const noOfPeers = Object.keys(stats.propagatedTo).length;
const noOfPropagations = Object.values(stats.propagatedTo).reduce((sum, val) => sum + val, 0);
return (
<span className={ styles.nowrap }>
{ noOfPropagations } ({ noOfPeers } peers)
2016-11-17 10:15:11 +01:00
</span>
);
}
}
export class Transaction extends BaseTransaction {
static propTypes = {
idx: PropTypes.number.isRequired,
transaction: PropTypes.object.isRequired,
2016-11-17 11:01:21 +01:00
blockNumber: PropTypes.object.isRequired,
2016-11-17 10:15:11 +01:00
isLocal: PropTypes.bool,
stats: PropTypes.object
};
static defaultProps = {
isLocal: false,
stats: {
firstSeen: 0,
propagatedTo: {}
}
};
static renderHeader () {
return (
<tr className={ styles.header }>
<th />
2016-11-17 10:15:11 +01:00
<th>
Transaction
</th>
<th>
From
</th>
<th>
Gas Price
</th>
<th>
Gas
</th>
<th>
2016-11-17 11:01:21 +01:00
First propagation
2016-11-17 10:15:11 +01:00
</th>
<th>
# Propagated
</th>
<th />
2016-11-17 10:15:11 +01:00
</tr>
);
}
render () {
const { isLocal, stats, transaction, idx } = this.props;
2016-11-17 14:00:53 +01:00
const blockNo = new BigNumber(stats.firstSeen);
2016-11-17 10:15:11 +01:00
const clazz = classnames(styles.transaction, {
[styles.local]: isLocal
});
return (
<tr className={ clazz }>
<td>
{ idx }.
</td>
<td>
{ this.renderHash(transaction.hash) }
</td>
<td>
{ this.renderFrom(transaction) }
</td>
<td>
{ this.renderGasPrice(transaction) }
</td>
<td>
{ this.renderGas(transaction) }
</td>
2016-11-17 11:01:21 +01:00
<td title={ blockNo.toFormat(0) }>
{ this.renderTime(stats.firstSeen) }
2016-11-17 10:15:11 +01:00
</td>
<td>
{ this.renderPropagation(stats) }
</td>
</tr>
);
}
2016-11-17 11:01:21 +01:00
renderTime (firstSeen) {
const { blockNumber } = this.props;
2016-11-17 11:01:21 +01:00
if (!firstSeen) {
return 'never';
}
const timeInMinutes = blockNumber.sub(firstSeen).mul(14).div(60).toFormat(1);
2016-11-17 11:01:21 +01:00
return `${timeInMinutes} minutes ago`;
}
2016-11-17 10:15:11 +01:00
}
export class LocalTransaction extends BaseTransaction {
static propTypes = {
hash: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
transaction: PropTypes.object,
isLocal: PropTypes.bool,
stats: PropTypes.object,
details: PropTypes.object
};
static defaultProps = {
stats: {
propagatedTo: {}
}
};
static renderHeader () {
return (
<tr className={ styles.header }>
<th />
2016-11-17 10:15:11 +01:00
<th>
Transaction
</th>
<th>
From
</th>
<th>
Gas Price
</th>
<th>
Gas
2016-11-17 10:15:11 +01:00
</th>
<th>
Status
</th>
</tr>
);
}
state = {
isSending: false,
isResubmitting: false,
gasPrice: null,
gas: null
};
toggleResubmit = () => {
const { transaction } = this.props;
const { isResubmitting } = this.state;
2016-11-17 10:15:11 +01:00
const nextState = {
2016-11-17 14:00:53 +01:00
isResubmitting: !isResubmitting
};
2016-11-17 10:15:11 +01:00
if (!isResubmitting) {
nextState.gasPrice = api.util.fromWei(transaction.gasPrice, 'shannon').toNumber();
nextState.gas = transaction.gas.div(1000000).toNumber();
2016-11-17 10:15:11 +01:00
}
this.setState(nextState);
2016-11-17 10:15:11 +01:00
};
2016-11-17 14:00:53 +01:00
setGasPrice = el => {
this.setState({
gasPrice: el.target.value
});
};
setGas = el => {
this.setState({
gas: el.target.value
});
};
2016-11-17 10:15:11 +01:00
sendTransaction = () => {
const { transaction, status } = this.props;
2016-11-17 10:15:11 +01:00
const { gasPrice, gas } = this.state;
const newTransaction = {
from: transaction.from,
value: transaction.value,
data: transaction.input,
gasPrice: api.util.toWei(gasPrice, 'shannon'),
gas: new BigNumber(gas).mul(1000000)
2016-11-17 10:15:11 +01:00
};
this.setState({
isResubmitting: false,
isSending: true
});
const closeSending = () => {
this.setState({
isSending: false,
gasPrice: null,
gas: null
});
};
if (transaction.to) {
newTransaction.to = transaction.to;
}
if (!['mined', 'replaced'].includes(status)) {
newTransaction.nonce = transaction.nonce;
}
2016-11-17 10:15:11 +01:00
api.eth.sendTransaction(newTransaction)
.then(closeSending)
.catch(closeSending);
};
render () {
if (this.state.isResubmitting) {
return this.renderResubmit();
}
const { stats, transaction, hash, status } = this.props;
const { isSending } = this.state;
const resubmit = isSending ? (
'sending...'
) : (
<button onClick={ this.toggleResubmit }>
2016-11-17 10:15:11 +01:00
resubmit
</button>
2016-11-17 10:15:11 +01:00
);
return (
<tr className={ styles.transaction }>
<td>
{ !transaction ? null : resubmit }
</td>
<td>
{ this.renderHash(hash) }
</td>
<td>
{ this.renderFrom(transaction) }
</td>
<td>
{ this.renderGasPrice(transaction) }
</td>
<td>
2016-11-17 10:15:11 +01:00
{ this.renderGas(transaction) }
</td>
<td>
{ this.renderStatus() }
2016-11-17 11:01:21 +01:00
<br />
{ status === 'pending' ? this.renderPropagation(stats) : null }
2016-11-17 10:15:11 +01:00
</td>
</tr>
);
}
renderStatus () {
const { details } = this.props;
let state = {
2016-11-17 14:00:53 +01:00
'pending': () => 'In queue: Pending',
'future': () => 'In queue: Future',
'mined': () => 'Mined',
'dropped': () => 'Dropped because of queue limit',
'invalid': () => 'Transaction is invalid',
'rejected': () => `Rejected: ${details.error}`,
'replaced': () => `Replaced by ${this.shortHash(details.hash)}`
2016-11-17 10:15:11 +01:00
}[this.props.status];
return state ? state() : 'unknown';
}
2016-11-17 14:00:53 +01:00
// TODO [ToDr] Gas Price / Gas selection is not needed
// when signer supports gasPrice/gas tunning.
2016-11-17 10:15:11 +01:00
renderResubmit () {
const { transaction } = this.props;
const { gasPrice, gas } = this.state;
return (
<tr className={ styles.transaction }>
<td>
<button onClick={ this.toggleResubmit }>
2016-11-17 10:15:11 +01:00
cancel
</button>
2016-11-17 10:15:11 +01:00
</td>
<td>
{ this.renderHash(transaction.hash) }
</td>
<td>
{ this.renderFrom(transaction) }
</td>
<td className={ styles.edit }>
<input
type='number'
2016-11-17 10:15:11 +01:00
value={ gasPrice }
2016-11-17 14:00:53 +01:00
onChange={ this.setGasPrice }
/>
<span>shannon</span>
</td>
<td className={ styles.edit }>
2016-11-17 10:15:11 +01:00
<input
type='number'
2016-11-17 10:15:11 +01:00
value={ gas }
2016-11-17 14:00:53 +01:00
onChange={ this.setGas }
/>
<span>MGas</span>
2016-11-17 10:15:11 +01:00
</td>
<td colSpan='2'>
<button onClick={ this.sendTransaction }>
2016-11-17 10:15:11 +01:00
Send
</button>
2016-11-17 10:15:11 +01:00
</td>
</tr>
);
}
}