Initial new UI source code import (#2607)

* address -> name mappings

* expanding, loading all coin details

* send use only actual BasicCoin tokens registered (any reg)

* sending token & accounts

* form styling updates

* send form layout in place

* coin send working as expected

* api subscriptions on multiple addresses

* bring in events

* simplify

* basic events display in-place, functionally complete

* basic functionality in-place

* fix horrible event address issue

* rwork display of events slightly

* test TLA availability

* table for owner -> tokens

* fix signature lookup address

* fix signature lookup address

* basic overview styling

* txhash links

* page layout adjustments

* background import

* adjust colors

* no global registration, simplify color selection

* updated styling

* connection dialog for "busy connecting"

* initial token connection - WIP

* init token updates take place

* basic test for manual token

* rework connection display

* allow updates of the secure token

* first stab at making the build build

* update runner tags

* fix linting issues

* skip tests requiring network (should be e2e, TODO)

* re-enable javascript tag/runner

* release push does the trick

* push to any branch, CI name

* javscript-test runner as well

* swap dependencies build requires test

* revert stages swap

* retrieve images associated with tokens

* remove js build deps order

* null image when hash = 0x0

* 6x64 images (hashes for registries)

* don't pass tokens as prop to IdentityIcon

* check images against content hash pictures

* cleanup signer after connection changes

* fix naming typo

* display unknownImages for balances (not available as content hash)

* unknownImage for transfer dialog

* basic githubhint layout

* single input for commit/filename

* ethcore_hashContent call

* lookup hash

* registration in place

* fixes

* events is using a proper table

* pass value through as-is

* stop wrongly using main app IdentityIcon

* NEVER export class instance functions

* alignment back to normal

* typo  in definition

* set & get images working (mostly)

* show content retrieval info

* set exitcode via ||

* use javascript:latest images

* disable npm progress bar

* rename phase I

* rename phase II

* only send build output to GitHub on major branches

* also run the build step as part of the test (until comprehensive)

* ci-specific build (no webpack progress)

* allow for account creation via recovery phrase

* display account uuid (where available), closes #2546

* connection dialog now shows up in dapps as well, closes #2538

* token images show up as expected

* IdentityName component added and deployed

* fix padding tests

* adjust tests to map to stricter 0x-prefixed hex

* render names via common component for the address -> name

* split lint into seperate script (early exit)

* test phases changed to lint, test & pack

* pack part of test phase

* remove files marked for deletion (cleanup)

* Signer cleanups, start moving in the direction of the rest

* add personal signer methods

* basic signer request subscription

* don't poll blockNumber when not connected

* missing return, creating massive ws queue backlogs

* ΞTH -> ETH

* fix failing tests

* registry uses setAddress to actually set addresses now

* bytes mapping operates on lowerCase hex strings

* sha3 ids for each application

* add dappreg to list of contracts

* adjust alignment of queries

* show gas estimation log

* abi with payable for register function

* add key as required

* image retrieval from dappreg

* use proper Image urls

* embed and link apps from Parity, retrieved via /api/apps

* filter apps that has been replaced

* proxy entry for parity-utils

* add basiccoin abi

* add support for fallback abi type

* capture constructor paramaters

* merge master into js

* move images to assets/images/

* add font assets

* import fonts as part of build

* don't inline woff files

* Revert "merge master into js"

This reverts commit cfcfa81bd26f1b3cbc748d3afa1eb5c670b363fe.

* remove unused npm packages

* information on gas estimates (like almost everywhere else)

* don't pass gas & gasPrice to estimation

* display account passwordhint when available

* signer subscriptions based on polling & function trapping

* pending requests retrieved via jsapi

* update signer middleware

* remove all web3 instances

* remove web3 package

* last web3 dependencies removed

* no need to toChecksumAddress - api takes care of it

* expand description for personal_confirmRequest

* Signer conversion from web3 -> parity.js completed

* explicit in no return

* green circle background

* remove generated background

* convert /api/* paths to localhost:8080/api/* paths (hard-coded, temporary)

* change dapps to load from localhost:8080/ui/*

* remove dangling web3 files

* update manager test for signer

* /api/ping -> /

* additional token images

* additional token images

* add missing styles.css for 8180 error pages

* cater for txhash returning null/empty object

* adjust output directories

* Release merge with origin with ours strategy

* additional token images

* cater for development server

* s/localhost/127.0.0.1/ (cater for origin)

* Fix address selection for contract deployment

* Adjust z-index for error overlay

* better text on unique background pattern

* fix signer rejections

* Don't allow gavcoin transfer with no balance

* fix txhash rendering in signer

* remove unnecessary ParityBackground

* script to update js-precompiled

* Redirect from :8080 to :8180

* Remove extra return

* Dapp logo images
This commit is contained in:
Jaco Greeff
2016-10-18 11:52:56 +02:00
committed by Gav Wood
parent 6c7af57529
commit 1e6a2cb378
969 changed files with 57315 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import React, { Component, PropTypes } from 'react';
import { Checkbox, MenuItem } from 'material-ui';
import Form, { Input, InputAddressSelect, Select } from '../../../ui/Form';
import imageUnknown from '../../../../assets/images/contracts/unknown-64x64.png';
import styles from '../transfer.css';
const CHECK_STYLE = {
position: 'absolute',
top: '38px',
left: '1em'
};
export default class Details extends Component {
static contextTypes = {
api: PropTypes.object
}
static propTypes = {
address: PropTypes.string,
balance: PropTypes.object,
all: PropTypes.bool,
extras: PropTypes.bool,
images: PropTypes.object.isRequired,
recipient: PropTypes.string,
recipientError: PropTypes.string,
tag: PropTypes.string,
total: PropTypes.string,
totalError: PropTypes.string,
value: PropTypes.string,
valueError: PropTypes.string,
onChange: PropTypes.func.isRequired
}
render () {
const { all, extras, tag, total, totalError, value, valueError } = this.props;
const label = `amount to transfer (in ${tag})`;
return (
<Form>
{ this.renderTokenSelect() }
{ this.renderToAddress() }
<div className={ styles.columns }>
<div>
<Input
disabled={ all }
label={ label }
hint='the amount to transfer to the recipient'
value={ value }
error={ valueError }
onChange={ this.onEditValue } />
</div>
<div>
<Checkbox
checked={ all }
label='full account balance'
onCheck={ this.onCheckAll }
style={ CHECK_STYLE } />
</div>
</div>
<div className={ styles.columns }>
<div>
<Input
disabled
label='total transaction amount'
error={ totalError }>
<div className={ styles.inputoverride }>
{ total }<small> ETH</small>
</div>
</Input>
</div>
<div>
<Checkbox
checked={ extras }
label='advanced sending options'
onCheck={ this.onCheckExtras }
style={ CHECK_STYLE } />
</div>
</div>
</Form>
);
}
renderToAddress () {
const { recipient, recipientError } = this.props;
return (
<div className={ styles.address }>
<InputAddressSelect
label='recipient address'
hint='the recipient address'
error={ recipientError }
value={ recipient }
onChange={ this.onEditRecipient } />
</div>
);
}
renderTokenSelect () {
const { api } = this.context;
const { balance, images, tag } = this.props;
const items = balance.tokens
.filter((token) => token.value.gt(0))
.map((balance, idx) => {
const token = balance.token;
const isEth = idx === 0;
const imagesrc = token.image || images[token.address] || imageUnknown;
let value = 0;
if (isEth) {
value = api.util.fromWei(balance.value).toFormat(3);
} else {
value = new BigNumber(balance.value).div(balance.token.format || 1).toFormat(3);
}
const label = (
<div className={ styles.token }>
<img src={ imagesrc } />
<div className={ styles.tokenname }>
{ token.name }
</div>
<div className={ styles.tokenbalance }>
{ value }<small> { token.tag }</small>
</div>
</div>
);
return (
<MenuItem
key={ token.tag }
value={ token.tag }
label={ label }>
{ label }
</MenuItem>
);
});
return (
<Select
label='type of token transfer'
hint='type of token to transfer'
value={ tag }
onChange={ this.onChangeToken }>
{ items }
</Select>
);
}
onChangeToken = (event, idx, tag) => {
this.props.onChange('tag', tag);
}
onEditRecipient = (event, recipient) => {
this.props.onChange('recipient', recipient);
}
onEditValue = (event) => {
this.props.onChange('value', event.target.value);
}
onCheckAll = () => {
this.props.onChange('all', !this.props.all);
}
onCheckExtras = () => {
this.props.onChange('extras', !this.props.extras);
}
onContacts = () => {
this.setState({
showAddresses: true
});
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
export default from './details';

View File

@@ -0,0 +1,109 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import Form, { Input } from '../../../ui/Form';
import styles from '../transfer.css';
export default class Extras extends Component {
static propTypes = {
isEth: PropTypes.bool,
data: PropTypes.string,
dataError: PropTypes.string,
gas: PropTypes.string,
gasEst: PropTypes.string,
gasError: PropTypes.string,
gasPrice: PropTypes.string,
gasPriceDefault: PropTypes.string,
gasPriceError: PropTypes.string,
total: PropTypes.string,
totalError: PropTypes.string,
onChange: PropTypes.func.isRequired
}
render () {
const { gas, gasError, gasEst, gasPrice, gasPriceDefault, gasPriceError, total, totalError } = this.props;
const gasLabel = `gas amount (estimated: ${gasEst})`;
const priceLabel = `gas price (current: ${gasPriceDefault})`;
return (
<Form>
{ this.renderData() }
<div className={ styles.columns }>
<div>
<Input
label={ gasLabel }
hint='the amount of gas to use for the transaction'
error={ gasError }
value={ gas }
onChange={ this.onEditGas } />
</div>
<div>
<Input
label={ priceLabel }
hint='the price of gas to use for the transaction'
error={ gasPriceError }
value={ gasPrice }
onChange={ this.onEditGasPrice } />
</div>
</div>
<div className={ styles.columns }>
<div>
<Input
disabled
label='total transaction amount'
hint='the total amount of the transaction'
error={ totalError }
value={ `${total} ETH` } />
</div>
</div>
</Form>
);
}
renderData () {
const { isEth, data, dataError } = this.props;
if (!isEth) {
return null;
}
return (
<div>
<Input
hint='the data to pass through with the transaction'
label='transaction data'
value={ data }
error={ dataError }
onChange={ this.onEditData } />
</div>
);
}
onEditGas = (event) => {
this.props.onChange('gas', event.target.value);
}
onEditGasPrice = (event) => {
this.props.onChange('gasPrice', event.target.value);
}
onEditData = (event) => {
this.props.onChange('data', event.target.value);
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
export default from './extras';

View File

@@ -0,0 +1,24 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
const ERRORS = {
requireRecipient: 'a recipient network address is required for the transaction',
invalidAddress: 'the supplied address is an invalid network address',
invalidAmount: 'the supplied amount should be a valid positive number',
largeAmount: 'the transaction total is higher than the available balance'
};
export default ERRORS;

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
export default from './transfer';

View File

@@ -0,0 +1,130 @@
/* Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
*/
.info {
line-height: 1.618em;
width: 100%;
}
.columns {
display: flex;
flex-wrap: wrap;
position: relative;
}
.columns>div {
flex: 0 1 50%;
width: 50%;
position: relative;
}
.floatbutton {
text-align: right;
float: right;
margin-left: -100%;
margin-top: 28px;
}
.floatbutton>div {
margin-right: 0.5em;
}
.token {
height: 32px;
margin-top: 14px;
}
.token img {
height: 32px;
width: 32px;
margin: 0 16px 0 0;
z-index: 10;
}
.token div {
height: 32px;
line-height: 32px;
display: inline-block;
vertical-align: top;
}
.tokenbalance {
display: inline-block;
color: #aaa;
padding-left: 1em;
}
.tokenname {}
.address {
position: relative;
}
.address input {
padding-left: 48px !important;
}
.from {
padding: 25px 0 0 48px !important;
line-height: 32px;
}
.fromaddress {
text-transform: uppercase;
display: inline-block;
}
.frombalance {
display: inline-block;
color: #aaa;
padding-left: 1em;
}
.icon {
position: absolute;
top: 35px;
}
.grayscale {
-webkit-filter: grayscale(1);
filter: grayscale(1);
opacity: 0;
}
.hdraccount {
padding: 1em 0 0 0;
}
.hdrimage {
display: inline-block;
}
.hdrdetails {
display: inline-block;
}
.hdrname {
text-transform: uppercase;
}
.hdraddress {
color: #aaa;
font-size: 0.75em;
}
.inputoverride {
padding-top: 24px !important;
}

View File

@@ -0,0 +1,577 @@
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import React, { Component, PropTypes } from 'react';
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
import Details from './Details';
import Extras from './Extras';
import ERRORS from './errors';
import styles from './transfer.css';
const DEFAULT_GAS = '21000';
const DEFAULT_GASPRICE = '20000000000';
const TITLES = {
transfer: 'transfer details',
sending: 'sending',
complete: 'complete',
extras: 'extra information'
};
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
export default class Transfer extends Component {
static contextTypes = {
api: PropTypes.object.isRequired,
store: PropTypes.object.isRequired
}
static propTypes = {
account: PropTypes.object,
balance: PropTypes.object,
balances: PropTypes.object,
images: PropTypes.object.isRequired,
onClose: PropTypes.func
}
state = {
stage: 0,
data: '',
dataError: null,
extras: false,
gas: DEFAULT_GAS,
gasEst: '0',
gasError: null,
gasPrice: DEFAULT_GASPRICE,
gasPriceError: null,
recipient: '',
recipientError: ERRORS.requireRecipient,
sending: false,
tag: 'ETH',
total: '0.0',
totalError: null,
value: '0.0',
valueAll: false,
valueError: null,
isEth: true,
busyState: null
}
componentDidMount () {
this.getDefaults();
}
render () {
const { stage, extras } = this.state;
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
steps={ extras ? STAGES_EXTRA : STAGES_BASIC }
waiting={ extras ? [2] : [1] }
visible>
{ this.renderPage() }
</Modal>
);
}
renderAccount () {
const { account } = this.props;
return (
<div className={ styles.hdraccount }>
<div className={ styles.hdrimage }>
<IdentityIcon
inline center
address={ account.address } />
</div>
<div className={ styles.hdrdetails }>
<div className={ styles.hdrname }>
{ account.name || 'Unnamed' }
</div>
<div className={ styles.hdraddress }>
{ account.address }
</div>
</div>
</div>
);
}
renderPage () {
const { extras, stage } = this.state;
if (stage === 0) {
return this.renderDetailsPage();
} else if (stage === 1 && extras) {
return this.renderExtrasPage();
}
return this.renderCompletePage();
}
renderCompletePage () {
const { sending, txhash, busyState } = this.state;
if (sending) {
return (
<BusyStep
title='The transaction is in progress'
state={ busyState } />
);
}
return (
<CompletedStep>
<TxHash hash={ txhash } />
</CompletedStep>
);
}
renderDetailsPage () {
const { account, balance, images } = this.props;
return (
<Details
address={ account.address }
all={ this.state.valueAll }
balance={ balance }
extras={ this.state.extras }
images={ images }
recipient={ this.state.recipient }
recipientError={ this.state.recipientError }
tag={ this.state.tag }
total={ this.state.total }
totalError={ this.state.totalError }
value={ this.state.value }
valueError={ this.state.valueError }
onChange={ this.onUpdateDetails } />
);
}
renderExtrasPage () {
return (
<Extras
isEth={ this.state.isEth }
data={ this.state.data }
dataError={ this.state.dataError }
gas={ this.state.gas }
gasEst={ this.state.gasEst }
gasError={ this.state.gasError }
gasPrice={ this.state.gasPrice }
gasPriceDefault={ this.state.gasPriceDefault }
gasPriceError={ this.state.gasPriceError }
total={ this.state.total }
totalError={ this.state.totalError }
onChange={ this.onUpdateDetails } />
);
}
renderDialogActions () {
const { account } = this.props;
const { extras, sending, stage } = this.state;
const cancelBtn = (
<Button
icon={ <ContentClear /> }
label='Cancel'
onClick={ this.onClose } />
);
const nextBtn = (
<Button
disabled={ !this.isValid() }
icon={ <NavigationArrowForward /> }
label='Next'
onClick={ this.onNext } />
);
const prevBtn = (
<Button
icon={ <NavigationArrowBack /> }
label='Back'
onClick={ this.onPrev } />
);
const sendBtn = (
<Button
disabled={ !this.isValid() || sending }
icon={ <IdentityIcon address={ account.address } button /> }
label='Send'
onClick={ this.onSend } />
);
const doneBtn = (
<Button
icon={ <ActionDoneAll /> }
label='Close'
onClick={ this.onClose } />
);
switch (stage) {
case 0:
return extras
? [cancelBtn, nextBtn]
: [cancelBtn, sendBtn];
case 1:
return extras
? [cancelBtn, prevBtn, sendBtn]
: [doneBtn];
default:
return [doneBtn];
}
}
isValid () {
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
const verifyValid = !this.state.passwordError;
switch (this.state.stage) {
case 0:
return detailsValid;
case 1:
return this.state.extras ? extrasValid : verifyValid;
case 2:
return verifyValid;
}
}
onNext = () => {
this.setState({
stage: this.state.stage + 1
});
}
onPrev = () => {
this.setState({
stage: this.state.stage - 1
});
}
_onUpdateAll (valueAll) {
this.setState({
valueAll
}, this.recalculateGas);
}
_onUpdateExtras (extras) {
this.setState({
extras
});
}
_onUpdateData (data) {
this.setState({
data
}, this.recalculateGas);
}
validatePositiveNumber (num) {
try {
const v = new BigNumber(num);
if (v.lt(0)) {
return ERRORS.invalidAmount;
}
} catch (e) {
return ERRORS.invalidAmount;
}
return null;
}
_onUpdateGas (gas) {
const gasError = this.validatePositiveNumber(gas);
this.setState({
gas,
gasError
}, this.recalculate);
}
_onUpdateGasPrice (gasPrice) {
const gasPriceError = this.validatePositiveNumber(gasPrice);
this.setState({
gasPrice,
gasPriceError
}, this.recalculate);
}
_onUpdateRecipient (recipient) {
const { api } = this.context;
let recipientError = null;
if (!recipient || !recipient.length) {
recipientError = ERRORS.requireRecipient;
} else if (!api.util.isAddressValid(recipient)) {
recipientError = ERRORS.invalidAddress;
}
this.setState({
recipient,
recipientError
}, this.recalculateGas);
}
_onUpdateTag (tag) {
const { balance } = this.props;
this.setState({
tag,
isEth: tag === balance.tokens[0].token.tag
}, this.recalculateGas);
}
_onUpdateValue (value) {
const valueError = this.validatePositiveNumber(value);
this.setState({
value,
valueError
}, this.recalculateGas);
}
onUpdateDetails = (type, value) => {
switch (type) {
case 'all':
return this._onUpdateAll(value);
case 'extras':
return this._onUpdateExtras(value);
case 'data':
return this._onUpdateData(value);
case 'gas':
return this._onUpdateGas(value);
case 'gasPrice':
return this._onUpdateGasPrice(value);
case 'recipient':
return this._onUpdateRecipient(value);
case 'tag':
return this._onUpdateTag(value);
case 'value':
return this._onUpdateValue(value);
}
}
_sendEth () {
const { api } = this.context;
const { account } = this.props;
const { data, gas, gasPrice, recipient, value } = this.state;
const options = {
from: account.address,
to: recipient,
gas,
gasPrice,
value: api.util.toWei(value || 0)
};
if (data && data.length) {
options.data = data;
}
return api.eth.postTransaction(options);
}
_sendToken () {
const { account, balance } = this.props;
const { recipient, value, tag } = this.state;
const token = balance.tokens.find((balance) => balance.token.tag === tag).token;
return token.contract.instance.transfer
.postTransaction({
from: account.address,
to: token.address
}, [
recipient,
new BigNumber(value).mul(token.format).toString()
]);
}
onSend = () => {
const { api } = this.context;
this.onNext();
this.setState({ sending: true }, () => {
(this.state.isEth
? this._sendEth()
: this._sendToken()
).then((requestId) => {
this.setState({ busyState: 'Waiting for authorization in the Parity Signer' });
return api.pollMethod('eth_checkRequest', requestId);
})
.then((txhash) => {
this.onNext();
this.setState({
sending: false,
txhash,
busyState: 'Your transaction has been posted to the network'
});
})
.catch((error) => {
console.log('send', error);
this.setState({
sending: false
});
this.newError(error);
});
});
}
onClose = () => {
this.setState({ stage: 0 }, () => {
this.props.onClose && this.props.onClose();
});
}
_estimateGasToken () {
const { account, balance } = this.props;
const { recipient, value, tag } = this.state;
const token = balance.tokens.find((balance) => balance.token.tag === tag).token;
return token.contract.instance.transfer
.estimateGas({
from: account.address,
to: token.address
}, [
recipient,
new BigNumber(value || 0).mul(token.format).toString()
]);
}
_estimateGasEth () {
const { api } = this.context;
const { account } = this.props;
const { data, recipient, value } = this.state;
const options = {
from: account.address,
to: recipient,
value: api.util.toWei(value || 0)
};
if (data && data.length) {
options.data = data;
}
return api.eth.estimateGas(options);
}
recalculateGas = () => {
(this.state.isEth
? this._estimateGasEth()
: this._estimateGasToken()
).then((_value) => {
let gas = _value;
if (gas.gt(DEFAULT_GAS)) {
gas = gas.mul(1.2);
}
this.setState({
gas: gas.toFixed(0),
gasEst: _value.toFormat()
}, this.recalculate);
})
.catch((error) => {
console.error('etimateGas', error);
});
}
recalculate = () => {
const { api } = this.context;
const { account, balance } = this.props;
if (!account || !balance) {
return;
}
const { gas, gasPrice, tag, valueAll, isEth } = this.state;
const gasTotal = new BigNumber(gasPrice || 0).mul(new BigNumber(gas || 0));
const balance_ = balance.tokens.find((b) => tag === b.token.tag);
const availableEth = new BigNumber(balance.tokens[0].value);
const available = new BigNumber(balance_.value);
const format = new BigNumber(balance_.token.format || 1);
let { value, valueError } = this.state;
let totalEth = gasTotal;
let totalError = null;
if (valueAll) {
if (isEth) {
const bn = api.util.fromWei(availableEth.minus(gasTotal));
value = (bn.lt(0) ? new BigNumber(0.0) : bn).toString();
} else {
value = available.div(format).toString();
}
}
if (isEth) {
totalEth = totalEth.plus(api.util.toWei(value || 0));
}
if (new BigNumber(value || 0).gt(available.div(format))) {
valueError = ERRORS.largeAmount;
} else if (valueError === ERRORS.largeAmount) {
valueError = null;
}
if (totalEth.gt(availableEth)) {
totalError = ERRORS.largeAmount;
}
this.setState({
total: api.util.fromWei(totalEth).toString(),
totalError,
value,
valueError
});
}
getDefaults = () => {
const { api } = this.context;
api.eth
.gasPrice()
.then((gasPrice) => {
this.setState({
gasPrice: gasPrice.toString(),
gasPriceDefault: gasPrice.toFormat()
}, this.recalculate);
})
.catch((error) => {
console.error('getDefaults', error);
});
}
newError = (error) => {
const { store } = this.context;
store.dispatch({ type: 'newError', error });
}
}