Speed up unresponsive Contract events & Account transactions (#3145)
* Don't load method via redux * Don't redux fetchTransaction (contracts) * Move isTest to top-level passing * transaction list with local blocks/transactions * Combine calls (allows future chunking) * Progressive loading of transactions * Cleanups * Never try tracing
This commit is contained in:
parent
29c8350bf2
commit
34e3c1e0c2
@ -19,12 +19,11 @@ import React, { Component, PropTypes } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
|
import Contracts from '../../contracts';
|
||||||
import IdentityIcon from '../IdentityIcon';
|
import IdentityIcon from '../IdentityIcon';
|
||||||
import IdentityName from '../IdentityName';
|
import IdentityName from '../IdentityName';
|
||||||
import { Input, InputAddress } from '../Form';
|
import { Input, InputAddress } from '../Form';
|
||||||
|
|
||||||
import { fetchBytecode, fetchMethod } from '../../redux/providers/blockchainActions';
|
|
||||||
|
|
||||||
import styles from './methodDecoding.css';
|
import styles from './methodDecoding.css';
|
||||||
|
|
||||||
const CONTRACT_CREATE = '0x60606040';
|
const CONTRACT_CREATE = '0x60606040';
|
||||||
@ -41,12 +40,7 @@ class MethodDecoding extends Component {
|
|||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
tokens: PropTypes.object,
|
tokens: PropTypes.object,
|
||||||
transaction: PropTypes.object,
|
transaction: PropTypes.object,
|
||||||
historic: PropTypes.bool,
|
historic: PropTypes.bool
|
||||||
|
|
||||||
fetchBytecode: PropTypes.func,
|
|
||||||
fetchMethod: PropTypes.func,
|
|
||||||
bytecodes: PropTypes.object,
|
|
||||||
methods: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -63,58 +57,7 @@ class MethodDecoding extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { transaction } = this.props;
|
this.lookup();
|
||||||
this.lookup(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
this.setMethod(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps (newProps) {
|
|
||||||
const { transaction } = this.props;
|
|
||||||
this.setMethod(newProps);
|
|
||||||
|
|
||||||
if (newProps.transaction.hash !== transaction.hash) {
|
|
||||||
this.lookup(transaction);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setMethod (props) {
|
|
||||||
const { bytecodes, methods } = props;
|
|
||||||
const { contractAddress, methodSignature, methodParams } = this.state;
|
|
||||||
|
|
||||||
if (contractAddress && bytecodes[contractAddress]) {
|
|
||||||
const bytecode = bytecodes[contractAddress];
|
|
||||||
|
|
||||||
if (bytecode && bytecode !== '0x') {
|
|
||||||
this.setState({ isContract: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodSignature && methods[methodSignature]) {
|
|
||||||
const method = methods[methodSignature];
|
|
||||||
const { api } = this.context;
|
|
||||||
|
|
||||||
let methodInputs = null;
|
|
||||||
let methodName = null;
|
|
||||||
|
|
||||||
if (method && method.length) {
|
|
||||||
const abi = api.util.methodToAbi(method);
|
|
||||||
|
|
||||||
methodName = abi.name;
|
|
||||||
methodInputs = api.util
|
|
||||||
.decodeMethodInput(abi, methodParams)
|
|
||||||
.map((value, index) => {
|
|
||||||
const type = abi.inputs[index].type;
|
|
||||||
|
|
||||||
return { type, value };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ method, methodName, methodInputs });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -321,7 +264,9 @@ class MethodDecoding extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup (transaction) {
|
lookup () {
|
||||||
|
const { transaction } = this.props;
|
||||||
|
|
||||||
if (!transaction) {
|
if (!transaction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -347,26 +292,51 @@ class MethodDecoding extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { fetchBytecode, fetchMethod } = this.props;
|
Promise
|
||||||
|
.all([
|
||||||
|
api.eth.getCode(contractAddress),
|
||||||
|
Contracts.get().signatureReg.lookup(signature)
|
||||||
|
])
|
||||||
|
.then(([bytecode, method]) => {
|
||||||
|
let methodInputs = null;
|
||||||
|
let methodName = null;
|
||||||
|
|
||||||
fetchBytecode(contractAddress);
|
if (method && method.length) {
|
||||||
fetchMethod(signature);
|
const { methodParams } = this.state;
|
||||||
|
const abi = api.util.methodToAbi(method);
|
||||||
|
|
||||||
|
methodName = abi.name;
|
||||||
|
methodInputs = api.util
|
||||||
|
.decodeMethodInput(abi, methodParams)
|
||||||
|
.map((value, index) => {
|
||||||
|
const type = abi.inputs[index].type;
|
||||||
|
|
||||||
|
return { type, value };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
method,
|
||||||
|
methodName,
|
||||||
|
methodInputs,
|
||||||
|
bytecode,
|
||||||
|
isContract: bytecode && bytecode !== '0x'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('lookup', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { tokens } = state.balances;
|
const { tokens } = state.balances;
|
||||||
const { bytecodes, methods } = state.blockchain;
|
|
||||||
|
|
||||||
return {
|
return { tokens };
|
||||||
tokens, bytecodes, methods
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
return bindActionCreators({
|
return bindActionCreators({}, dispatch);
|
||||||
fetchBytecode, fetchMethod
|
|
||||||
}, dispatch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
@ -17,46 +17,37 @@
|
|||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
|
|
||||||
import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions';
|
|
||||||
|
|
||||||
import { IdentityIcon, IdentityName, MethodDecoding } from '../../../../ui';
|
import { IdentityIcon, IdentityName, MethodDecoding } from '../../../../ui';
|
||||||
import { txLink, addressLink } from '../../../../3rdparty/etherscan/links';
|
import { txLink, addressLink } from '../../../../3rdparty/etherscan/links';
|
||||||
|
|
||||||
import styles from '../transactions.css';
|
import styles from '../transactions.css';
|
||||||
|
|
||||||
class Transaction extends Component {
|
export default class Transaction extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired
|
api: PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
transaction: PropTypes.object.isRequired,
|
|
||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
isTest: PropTypes.bool.isRequired,
|
isTest: PropTypes.bool.isRequired,
|
||||||
|
transaction: PropTypes.object.isRequired
|
||||||
fetchBlock: PropTypes.func.isRequired,
|
|
||||||
fetchTransaction: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
block: PropTypes.object,
|
|
||||||
transactionInfo: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
isContract: false,
|
isContract: false,
|
||||||
isReceived: false
|
isReceived: false,
|
||||||
|
transaction: null,
|
||||||
|
block: null
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { address, transaction } = this.props;
|
this.lookup();
|
||||||
|
|
||||||
this.lookup(address, transaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { block, transaction } = this.props;
|
const { block } = this.state;
|
||||||
|
const { transaction } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
@ -75,9 +66,10 @@ class Transaction extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderMethod () {
|
renderMethod () {
|
||||||
const { address, transactionInfo } = this.props;
|
const { address } = this.props;
|
||||||
|
const { transaction } = this.state;
|
||||||
|
|
||||||
if (!transactionInfo) {
|
if (!transaction) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,12 +77,13 @@ class Transaction extends Component {
|
|||||||
<MethodDecoding
|
<MethodDecoding
|
||||||
historic
|
historic
|
||||||
address={ address }
|
address={ address }
|
||||||
transaction={ transactionInfo } />
|
transaction={ transaction } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTransaction () {
|
renderTransaction () {
|
||||||
const { transaction, isTest } = this.props;
|
const { isTest } = this.props;
|
||||||
|
const { transaction } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<td className={ styles.transaction }>
|
<td className={ styles.transaction }>
|
||||||
@ -138,13 +131,13 @@ class Transaction extends Component {
|
|||||||
|
|
||||||
renderEtherValue () {
|
renderEtherValue () {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
const { transactionInfo } = this.props;
|
const { transaction } = this.state;
|
||||||
|
|
||||||
if (!transactionInfo) {
|
if (!transaction) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = api.util.fromWei(transactionInfo.value);
|
const value = api.util.fromWei(transaction.value);
|
||||||
|
|
||||||
if (value.eq(0)) {
|
if (value.eq(0)) {
|
||||||
return <div className={ styles.value }>{ ' ' }</div>;
|
return <div className={ styles.value }>{ ' ' }</div>;
|
||||||
@ -177,34 +170,22 @@ class Transaction extends Component {
|
|||||||
return moment(block.timestamp).fromNow();
|
return moment(block.timestamp).fromNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup (address, transaction) {
|
lookup () {
|
||||||
const { transactionInfo } = this.props;
|
const { api } = this.context;
|
||||||
|
const { transaction, address } = this.props;
|
||||||
if (transactionInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ isReceived: address === transaction.to });
|
this.setState({ isReceived: address === transaction.to });
|
||||||
|
|
||||||
const { fetchBlock, fetchTransaction } = this.props;
|
Promise
|
||||||
const { blockNumber, hash } = transaction;
|
.all([
|
||||||
|
api.eth.getBlockByNumber(transaction.blockNumber),
|
||||||
fetchBlock(blockNumber);
|
api.eth.getTransactionByHash(transaction.hash)
|
||||||
fetchTransaction(hash);
|
])
|
||||||
|
.then(([block, transaction]) => {
|
||||||
|
this.setState({ block, transaction });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('lookup', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps () {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return bindActionCreators({
|
|
||||||
fetchBlock, fetchTransaction
|
|
||||||
}, dispatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(Transaction);
|
|
||||||
|
@ -38,9 +38,7 @@ class Transactions extends Component {
|
|||||||
contracts: PropTypes.object,
|
contracts: PropTypes.object,
|
||||||
tokens: PropTypes.object,
|
tokens: PropTypes.object,
|
||||||
isTest: PropTypes.bool,
|
isTest: PropTypes.bool,
|
||||||
traceMode: PropTypes.bool,
|
traceMode: PropTypes.bool
|
||||||
blocks: PropTypes.object,
|
|
||||||
transactionsInfo: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -121,7 +119,7 @@ class Transactions extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderRows () {
|
renderRows () {
|
||||||
const { address, accounts, contacts, contracts, tokens, isTest, blocks, transactionsInfo } = this.props;
|
const { address, accounts, contacts, contracts, tokens, isTest } = this.props;
|
||||||
const { transactions } = this.state;
|
const { transactions } = this.state;
|
||||||
|
|
||||||
return (transactions || [])
|
return (transactions || [])
|
||||||
@ -130,16 +128,9 @@ class Transactions extends Component {
|
|||||||
})
|
})
|
||||||
.slice(0, 25)
|
.slice(0, 25)
|
||||||
.map((transaction, index) => {
|
.map((transaction, index) => {
|
||||||
const { blockNumber, hash } = transaction;
|
|
||||||
|
|
||||||
const block = blocks[blockNumber.toString()];
|
|
||||||
const transactionInfo = transactionsInfo[hash];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transaction
|
<Transaction
|
||||||
key={ index }
|
key={ index }
|
||||||
block={ block }
|
|
||||||
transactionInfo={ transactionInfo }
|
|
||||||
transaction={ transaction }
|
transaction={ transaction }
|
||||||
address={ address }
|
address={ address }
|
||||||
accounts={ accounts }
|
accounts={ accounts }
|
||||||
@ -165,9 +156,9 @@ class Transactions extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchTransactions = (isTest, address, traceMode) => {
|
fetchTransactions = (isTest, address, traceMode) => {
|
||||||
if (traceMode) {
|
// if (traceMode) {
|
||||||
return this.fetchTraceTransactions(address);
|
// return this.fetchTraceTransactions(address);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return this.fetchEtherscanTransactions(isTest, address);
|
return this.fetchEtherscanTransactions(isTest, address);
|
||||||
}
|
}
|
||||||
@ -211,7 +202,6 @@ function mapStateToProps (state) {
|
|||||||
const { isTest, traceMode } = state.nodeStatus;
|
const { isTest, traceMode } = state.nodeStatus;
|
||||||
const { accounts, contacts, contracts } = state.personal;
|
const { accounts, contacts, contracts } = state.personal;
|
||||||
const { tokens } = state.balances;
|
const { tokens } = state.balances;
|
||||||
const { blocks, transactions } = state.blockchain;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isTest,
|
isTest,
|
||||||
@ -219,9 +209,7 @@ function mapStateToProps (state) {
|
|||||||
accounts,
|
accounts,
|
||||||
contacts,
|
contacts,
|
||||||
contracts,
|
contracts,
|
||||||
tokens,
|
tokens
|
||||||
blocks,
|
|
||||||
transactionsInfo: transactions
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,27 +17,24 @@
|
|||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
|
|
||||||
import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions';
|
|
||||||
import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui';
|
import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui';
|
||||||
import { txLink } from '../../../../3rdparty/etherscan/links';
|
import { txLink } from '../../../../3rdparty/etherscan/links';
|
||||||
|
|
||||||
import styles from '../../contract.css';
|
import styles from '../../contract.css';
|
||||||
|
|
||||||
class Event extends Component {
|
export default class Event extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired
|
api: PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
event: PropTypes.object.isRequired,
|
event: PropTypes.object.isRequired,
|
||||||
blocks: PropTypes.object,
|
isTest: PropTypes.bool
|
||||||
transactions: PropTypes.object,
|
}
|
||||||
isTest: PropTypes.bool,
|
|
||||||
fetchBlock: PropTypes.func.isRequired,
|
state = {
|
||||||
fetchTransaction: PropTypes.func.isRequired
|
transaction: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@ -45,10 +42,9 @@ class Event extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { event, blocks, transactions, isTest } = this.props;
|
const { event, isTest } = this.props;
|
||||||
|
const { block, transaction } = this.state;
|
||||||
|
|
||||||
const block = blocks[event.blockNumber.toString()];
|
|
||||||
const transaction = transactions[event.transactionHash] || {};
|
|
||||||
const classes = `${styles.event} ${styles[event.state]}`;
|
const classes = `${styles.event} ${styles[event.state]}`;
|
||||||
const url = txLink(event.transactionHash, isTest);
|
const url = txLink(event.transactionHash, isTest);
|
||||||
const keys = Object.keys(event.params).join(', ');
|
const keys = Object.keys(event.params).join(', ');
|
||||||
@ -156,31 +152,19 @@ class Event extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
retrieveTransaction () {
|
retrieveTransaction () {
|
||||||
const { event, fetchBlock, fetchTransaction } = this.props;
|
const { api } = this.context;
|
||||||
|
const { event } = this.props;
|
||||||
|
|
||||||
fetchBlock(event.blockNumber);
|
Promise
|
||||||
fetchTransaction(event.transactionHash);
|
.all([
|
||||||
|
api.eth.getBlockByNumber(event.blockNumber),
|
||||||
|
api.eth.getTransactionByHash(event.transactionHash)
|
||||||
|
])
|
||||||
|
.then(([block, transaction]) => {
|
||||||
|
this.setState({ block, transaction });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('retrieveTransaction', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
const { isTest } = state.nodeStatus;
|
|
||||||
const { blocks, transactions } = state.blockchain;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isTest,
|
|
||||||
blocks,
|
|
||||||
transactions
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return bindActionCreators({
|
|
||||||
fetchBlock, fetchTransaction
|
|
||||||
}, dispatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(Event);
|
|
||||||
|
@ -27,21 +27,31 @@ export default class Events extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
events: PropTypes.array
|
events: PropTypes.array,
|
||||||
|
isTest: PropTypes.bool.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { events } = this.props;
|
const { events, isTest } = this.props;
|
||||||
|
|
||||||
if (!events || !events.length) {
|
if (!events || !events.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const list = events.map((event) => {
|
||||||
|
return (
|
||||||
|
<Event
|
||||||
|
key={ event.key }
|
||||||
|
event={ event }
|
||||||
|
isTest={ isTest } />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<ContainerTitle title='events' />
|
<ContainerTitle title='events' />
|
||||||
<table className={ styles.events }>
|
<table className={ styles.events }>
|
||||||
<tbody>{ events.map((event) => <Event event={ event } key={ event.key } />) }</tbody>
|
<tbody>{ list }</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -116,6 +116,7 @@ class Contract extends Component {
|
|||||||
contract={ contract }
|
contract={ contract }
|
||||||
values={ queryValues } />
|
values={ queryValues } />
|
||||||
<Events
|
<Events
|
||||||
|
isTest={ isTest }
|
||||||
events={ allEvents } />
|
events={ allEvents } />
|
||||||
</Page>
|
</Page>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user