// Copyright 2015-2017 Parity Technologies (UK) Ltd. // 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 . import { CircularProgress } from 'material-ui'; import moment from 'moment'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { TypedInput, InputAddress } from '../Form'; import MethodDecodingStore from './methodDecodingStore'; import styles from './methodDecoding.css'; const ASCII_INPUT = /^[a-z0-9\s,?;.:/!()-_@'"#]+$/i; const TOKEN_METHODS = { '0xa9059cbb': 'transfer(to,value)' }; class MethodDecoding extends Component { static contextTypes = { api: PropTypes.object.isRequired } static propTypes = { address: PropTypes.string.isRequired, token: PropTypes.object, transaction: PropTypes.object, historic: PropTypes.bool } state = { contractAddress: null, methodName: null, methodInputs: null, methodParams: null, methodSignature: null, isContract: false, isDeploy: false, isReceived: false, isLoading: true, expandInput: false, inputType: 'auto' } methodDecodingStore = MethodDecodingStore.get(this.context.api); componentWillMount () { const { address, transaction } = this.props; this .methodDecodingStore .lookup(address, transaction) .then((lookup) => { const newState = { methodName: lookup.name, methodInputs: lookup.inputs, methodParams: lookup.params, methodSignature: lookup.signature, isContract: lookup.contract, isDeploy: lookup.deploy, isLoading: false, isReceived: lookup.received }; this.setState(newState); }); } render () { const { transaction } = this.props; const { isLoading } = this.state; if (!transaction) { return null; } if (isLoading) { return (
); } return (
{ this.renderAction() } { this.renderGas() }
); } renderGas () { const { historic, transaction } = this.props; const { gas, gasPrice, value } = transaction; if (!gas || !gasPrice) { return null; } const gasProvided = ( ETH } } /> ); const totalEthValue = ( { this.renderEtherValue(gas.mul(gasPrice).plus(value || 0)) } ); const gasUsed = transaction.gasUsed ? ( ) : ''; return (
{ this.renderMinBlock() }
); } renderMinBlock () { const { historic, transaction } = this.props; const { condition } = transaction; if (!condition) { return null; } if (condition.block && condition.block.gt(0)) { const blockNumber = ( #{ condition.block.toFormat(0) } ); return ( ); } if (condition.time) { const timestamp = ( { moment(condition.time).format('LLLL') } ); return ( ); } return null; } renderAction () { const { token } = this.props; const { methodName, methodInputs, methodSignature, isDeploy, isReceived, isContract } = this.state; if (isDeploy) { return this.renderDeploy(); } if (isContract && methodSignature) { if (token && TOKEN_METHODS[methodSignature] && methodInputs) { return this.renderTokenAction(); } if (methodName) { return this.renderSignatureMethod(); } return this.renderUnknownMethod(); } return isReceived ? this.renderValueReceipt() : this.renderValueTransfer(); } getAscii () { const { api } = this.context; const { transaction } = this.props; const value = api.util.hexToAscii(transaction.input || transaction.data); return { value, valid: ASCII_INPUT.test(value) }; } renderInputValue () { const { transaction } = this.props; const { expandInput, inputType } = this.state; const input = transaction.input || transaction.data; if (!/^(0x)?([0]*[1-9a-f]+[0]*)+$/.test(input)) { return null; } const ascii = this.getAscii(); const type = inputType === 'auto' ? (ascii.valid ? 'ascii' : 'raw') : inputType; const text = type === 'ascii' ? ascii.value : input; const expandable = text.length > 50; const textToShow = expandInput || !expandable ? text : text.slice(0, 50) + '...'; const inputDesc = ( { type === 'ascii' ? ( ) : ( ) } ); const inputValue = ( { textToShow } ); return (
); } renderTokenAction () { const { historic } = this.props; const { methodSignature, methodInputs } = this.state; const [to, value] = methodInputs; const address = to.value; switch (TOKEN_METHODS[methodSignature]) { case 'transfer(to,value)': default: return (
{ this.renderTokenValue(value.value) } ), address: this.renderAddressName(address) } } />
); } } renderDeploy () { const { historic, transaction } = this.props; const { methodInputs } = this.state; const { value } = transaction; if (!historic) { return (
{ value && value.gt(0) ? ( ) : null }
); } return (
{ this.renderAddressName(transaction.creates, false) }
{ methodInputs && methodInputs.length ? ( ) : '' }
{ this.renderInputs() }
); } renderValueReceipt () { const { historic, transaction } = this.props; const { isContract } = this.state; const valueEth = ( { this.renderEtherValue(transaction.value) } ); const aContract = isContract ? ( ) : ''; return (
{ this.renderInputValue() }
); } renderValueTransfer () { const { historic, transaction } = this.props; const { isContract } = this.state; const valueEth = ( { this.renderEtherValue(transaction.value) } ); const aContract = isContract ? ( ) : ''; return (
{ this.renderInputValue() }
); } renderSignatureMethod () { const { historic, transaction } = this.props; const { methodName, methodInputs } = this.state; const method = ( { methodName } ); const ethValue = ( { this.renderEtherValue(transaction.value) } ); return (
{ this.renderInputs() }
); } renderUnknownMethod () { const { historic, transaction } = this.props; const method = ( an unknown/unregistered ); const ethValue = ( { this.renderEtherValue(transaction.value) } ); return (
); } renderInputs () { const { methodInputs } = this.state; if (!methodInputs || methodInputs.length === 0) { return null; } const inputs = methodInputs.map((input, index) => { const label = input.name ? `${input.name}: ${input.type}` : input.type; return ( ); }); return inputs; } renderTokenValue (value) { const { token } = this.props; return ( { value.div(token.format).toFormat(5) } { token.tag } ); } renderEtherValue (value) { const { api } = this.context; const ether = api.util.fromWei(value); return ( { ether.toFormat(5) } ETH ); } renderAddressName (address, withName = true) { return (
); } toggleInputExpand = () => { if (window.getSelection && window.getSelection().type === 'Range') { return; } this.setState({ expandInput: !this.state.expandInput }); } toggleInputType = () => { const { inputType } = this.state; if (inputType !== 'auto') { return this.setState({ inputType: this.state.inputType === 'raw' ? 'ascii' : 'raw' }); } const ascii = this.getAscii(); return this.setState({ inputType: ascii.valid ? 'raw' : 'ascii' }); } } function mapStateToProps (initState, initProps) { const { tokens } = initState.balances; const { address } = initProps; const token = (tokens || {})[address]; return () => { return { token }; }; } export default connect( mapStateToProps, null )(MethodDecoding);