2017-01-25 18:51:41 +01:00
|
|
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
2016-12-02 15:21:01 +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/>.
|
|
|
|
|
|
|
|
import { observable, computed, action, transaction } from 'mobx';
|
|
|
|
import BigNumber from 'bignumber.js';
|
2016-12-07 12:47:44 +01:00
|
|
|
import { uniq } from 'lodash';
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
import { wallet as walletAbi } from '~/contracts/abi';
|
|
|
|
import { bytesToHex } from '~/api/util/format';
|
|
|
|
import Contract from '~/api/contract';
|
2016-12-02 15:21:01 +01:00
|
|
|
import ERRORS from './errors';
|
2016-12-05 11:47:13 +01:00
|
|
|
import { ERROR_CODES } from '~/api/transport/error';
|
2017-01-11 17:01:35 +01:00
|
|
|
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants';
|
2016-12-09 13:44:35 +01:00
|
|
|
import GasPriceStore from '~/ui/GasPriceEditor/store';
|
2017-01-10 13:26:30 +01:00
|
|
|
import { getLogger, LOG_KEYS } from '~/config';
|
|
|
|
|
|
|
|
const log = getLogger(LOG_KEYS.TransferModalStore);
|
2016-12-02 15:21:01 +01:00
|
|
|
|
|
|
|
const TITLES = {
|
|
|
|
transfer: 'transfer details',
|
|
|
|
sending: 'sending',
|
|
|
|
complete: 'complete',
|
|
|
|
extras: 'extra information',
|
|
|
|
rejected: 'rejected'
|
|
|
|
};
|
|
|
|
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
|
|
|
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
|
|
|
|
|
|
|
export default class TransferStore {
|
|
|
|
@observable stage = 0;
|
2016-12-06 09:37:59 +01:00
|
|
|
@observable extras = false;
|
|
|
|
@observable valueAll = false;
|
|
|
|
@observable sending = false;
|
|
|
|
@observable tag = 'ETH';
|
|
|
|
@observable isEth = true;
|
|
|
|
@observable busyState = null;
|
|
|
|
@observable rejected = false;
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@observable data = '';
|
|
|
|
@observable dataError = null;
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@observable recipient = '';
|
|
|
|
@observable recipientError = ERRORS.requireRecipient;
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
@observable sender = '';
|
|
|
|
@observable senderError = null;
|
2016-12-10 16:46:00 +01:00
|
|
|
@observable sendersBalances = {};
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@observable total = '0.0';
|
|
|
|
@observable totalError = null;
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@observable value = '0.0';
|
|
|
|
@observable valueError = null;
|
|
|
|
|
|
|
|
account = null;
|
|
|
|
balance = null;
|
|
|
|
onClose = null;
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
senders = null;
|
2016-12-06 09:37:59 +01:00
|
|
|
isWallet = false;
|
|
|
|
wallet = null;
|
|
|
|
|
2016-12-09 13:44:35 +01:00
|
|
|
gasStore = null;
|
|
|
|
|
2017-02-03 20:01:09 +01:00
|
|
|
constructor (api, props) {
|
|
|
|
this.api = api;
|
|
|
|
|
|
|
|
const { account, balance, gasLimit, senders, newError, sendersBalances } = props;
|
|
|
|
|
|
|
|
this.account = account;
|
|
|
|
this.balance = balance;
|
|
|
|
this.isWallet = account && account.wallet;
|
|
|
|
this.newError = newError;
|
|
|
|
|
|
|
|
this.gasStore = new GasPriceStore(api, { gasLimit });
|
|
|
|
|
|
|
|
if (this.isWallet) {
|
|
|
|
this.wallet = props.wallet;
|
|
|
|
this.walletContract = new Contract(this.api, walletAbi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (senders) {
|
|
|
|
this.senders = senders;
|
|
|
|
this.sendersBalances = sendersBalances;
|
|
|
|
this.senderError = ERRORS.requireSender;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@computed get steps () {
|
|
|
|
const steps = [].concat(this.extras ? STAGES_EXTRA : STAGES_BASIC);
|
|
|
|
|
|
|
|
if (this.rejected) {
|
|
|
|
steps[steps.length - 1] = TITLES.rejected;
|
|
|
|
}
|
|
|
|
|
|
|
|
return steps;
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get isValid () {
|
2016-12-06 09:37:59 +01:00
|
|
|
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
2017-02-03 20:01:09 +01:00
|
|
|
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.gasStore.conditionBlockError && !this.totalError;
|
2016-12-02 15:21:01 +01:00
|
|
|
const verifyValid = !this.passwordError;
|
|
|
|
|
|
|
|
switch (this.stage) {
|
|
|
|
case 0:
|
|
|
|
return detailsValid;
|
|
|
|
|
|
|
|
case 1:
|
2016-12-23 15:31:19 +01:00
|
|
|
return this.extras
|
|
|
|
? extrasValid
|
|
|
|
: verifyValid;
|
2016-12-02 15:21:01 +01:00
|
|
|
|
|
|
|
case 2:
|
|
|
|
return verifyValid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
get token () {
|
|
|
|
return this.balance.tokens.find((balance) => balance.token.tag === this.tag).token;
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@action onNext = () => {
|
|
|
|
this.stage += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@action onPrev = () => {
|
|
|
|
this.stage -= 1;
|
|
|
|
}
|
|
|
|
|
2016-12-10 01:25:23 +01:00
|
|
|
@action handleClose = () => {
|
2016-12-02 15:21:01 +01:00
|
|
|
this.stage = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@action 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);
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
case 'sender':
|
|
|
|
return this._onUpdateSender(value);
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
case 'tag':
|
|
|
|
return this._onUpdateTag(value);
|
|
|
|
|
|
|
|
case 'value':
|
|
|
|
return this._onUpdateValue(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@action onSend = () => {
|
|
|
|
this.onNext();
|
|
|
|
this.sending = true;
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
this
|
|
|
|
.send()
|
2016-12-02 15:21:01 +01:00
|
|
|
.then((requestId) => {
|
|
|
|
this.busyState = 'Waiting for authorization in the Parity Signer';
|
|
|
|
|
|
|
|
return this.api
|
|
|
|
.pollMethod('parity_checkRequest', requestId)
|
|
|
|
.catch((e) => {
|
|
|
|
if (e.code === ERROR_CODES.REQUEST_REJECTED) {
|
|
|
|
this.rejected = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw e;
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then((txhash) => {
|
|
|
|
transaction(() => {
|
|
|
|
this.onNext();
|
|
|
|
|
|
|
|
this.sending = false;
|
|
|
|
this.txhash = txhash;
|
|
|
|
this.busyState = 'Your transaction has been posted to the network';
|
|
|
|
});
|
2016-12-07 12:47:44 +01:00
|
|
|
|
|
|
|
if (this.isWallet) {
|
|
|
|
return this._attachWalletOperation(txhash);
|
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
this.sending = false;
|
|
|
|
this.newError(error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
@action _attachWalletOperation = (txhash) => {
|
2017-01-13 15:52:24 +01:00
|
|
|
if (!txhash || /^(0x)?0*$/.test(txhash)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
let ethSubscriptionId = null;
|
|
|
|
|
2017-01-13 15:52:24 +01:00
|
|
|
// Number of blocks left to look-up (unsub after 15 blocks if nothing)
|
|
|
|
let nBlocksLeft = 15;
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
return this.api.subscribe('eth_blockNumber', () => {
|
|
|
|
this.api.eth
|
|
|
|
.getTransactionReceipt(txhash)
|
|
|
|
.then((tx) => {
|
2017-01-13 15:52:24 +01:00
|
|
|
if (nBlocksLeft <= 0) {
|
|
|
|
this.api.unsubscribe(ethSubscriptionId);
|
|
|
|
ethSubscriptionId = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (!tx) {
|
2017-01-13 15:52:24 +01:00
|
|
|
nBlocksLeft--;
|
2016-12-07 12:47:44 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const logs = this.walletContract.parseEventLogs(tx.logs);
|
|
|
|
const operations = uniq(logs
|
|
|
|
.filter((log) => log && log.params && log.params.operation)
|
|
|
|
.map((log) => bytesToHex(log.params.operation.value)));
|
|
|
|
|
|
|
|
if (operations.length > 0) {
|
|
|
|
this.operation = operations[0];
|
|
|
|
}
|
|
|
|
|
2017-01-13 15:52:24 +01:00
|
|
|
this.api.unsubscribe(ethSubscriptionId);
|
|
|
|
ethSubscriptionId = null;
|
|
|
|
})
|
|
|
|
.catch(() => {
|
2016-12-07 12:47:44 +01:00
|
|
|
this.api.unsubscribe(ethSubscriptionId);
|
|
|
|
ethSubscriptionId = null;
|
|
|
|
});
|
|
|
|
}).then((subId) => {
|
|
|
|
ethSubscriptionId = subId;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@action _onUpdateAll = (valueAll) => {
|
|
|
|
this.valueAll = valueAll;
|
|
|
|
this.recalculateGas();
|
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateExtras = (extras) => {
|
|
|
|
this.extras = extras;
|
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateData = (data) => {
|
|
|
|
this.data = data;
|
|
|
|
this.recalculateGas();
|
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateGas = (gas) => {
|
2016-12-09 13:44:35 +01:00
|
|
|
this.recalculate();
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateGasPrice = (gasPrice) => {
|
2016-12-09 13:44:35 +01:00
|
|
|
this.recalculate();
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateRecipient = (recipient) => {
|
|
|
|
let recipientError = null;
|
|
|
|
|
|
|
|
if (!recipient || !recipient.length) {
|
|
|
|
recipientError = ERRORS.requireRecipient;
|
|
|
|
} else if (!this.api.util.isAddressValid(recipient)) {
|
|
|
|
recipientError = ERRORS.invalidAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction(() => {
|
|
|
|
this.recipient = recipient;
|
|
|
|
this.recipientError = recipientError;
|
|
|
|
|
|
|
|
this.recalculateGas();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
@action _onUpdateSender = (sender) => {
|
|
|
|
let senderError = null;
|
|
|
|
|
|
|
|
if (!sender || !sender.length) {
|
|
|
|
senderError = ERRORS.requireSender;
|
|
|
|
} else if (!this.api.util.isAddressValid(sender)) {
|
|
|
|
senderError = ERRORS.invalidAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction(() => {
|
|
|
|
this.sender = sender;
|
|
|
|
this.senderError = senderError;
|
|
|
|
|
|
|
|
this.recalculateGas();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
@action _onUpdateTag = (tag) => {
|
|
|
|
transaction(() => {
|
|
|
|
this.tag = tag;
|
|
|
|
this.isEth = tag.toLowerCase().trim() === 'eth';
|
|
|
|
|
|
|
|
this.recalculateGas();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@action _onUpdateValue = (value) => {
|
|
|
|
let valueError = this._validatePositiveNumber(value);
|
|
|
|
|
|
|
|
if (!valueError) {
|
|
|
|
valueError = this._validateDecimals(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction(() => {
|
|
|
|
this.value = value;
|
|
|
|
this.valueError = valueError;
|
|
|
|
|
|
|
|
this.recalculateGas();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
@action recalculateGas = (redo = true) => {
|
2016-12-02 15:21:01 +01:00
|
|
|
if (!this.isValid) {
|
2017-01-10 13:26:30 +01:00
|
|
|
return this.recalculate(redo);
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
return this
|
2016-12-06 09:37:59 +01:00
|
|
|
.estimateGas()
|
2016-12-02 15:21:01 +01:00
|
|
|
.then((gasEst) => {
|
|
|
|
let gas = gasEst;
|
|
|
|
|
|
|
|
if (gas.gt(DEFAULT_GAS)) {
|
|
|
|
gas = gas.mul(1.2);
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction(() => {
|
2016-12-09 13:44:35 +01:00
|
|
|
this.gasStore.setEstimated(gasEst.toFixed(0));
|
|
|
|
this.gasStore.setGas(gas.toFixed(0));
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
this.recalculate(redo);
|
2016-12-02 15:21:01 +01:00
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
2017-01-12 13:56:37 +01:00
|
|
|
this.gasStore.setEstimatedError();
|
2016-12-09 13:44:35 +01:00
|
|
|
console.warn('etimateGas', error);
|
2017-01-10 13:26:30 +01:00
|
|
|
this.recalculate(redo);
|
2016-12-02 15:21:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
getBalance (forceSender = false) {
|
|
|
|
if (this.isWallet && !forceSender) {
|
|
|
|
return this.balance;
|
2016-12-07 12:47:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const balance = this.senders
|
|
|
|
? this.sendersBalances[this.sender]
|
|
|
|
: this.balance;
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
return balance;
|
|
|
|
}
|
|
|
|
|
|
|
|
getToken (tag = this.tag, forceSender = false) {
|
|
|
|
const balance = this.getBalance(forceSender);
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (!balance) {
|
2017-01-10 13:26:30 +01:00
|
|
|
return null;
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
const _tag = tag.toLowerCase();
|
|
|
|
const token = balance.tokens.find((b) => b.token.tag.toLowerCase() === _tag);
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
return token;
|
|
|
|
}
|
2016-12-07 12:47:44 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
/**
|
|
|
|
* Return the balance of the selected token
|
|
|
|
* (in WEI for ETH, without formating for other tokens)
|
|
|
|
*/
|
|
|
|
getTokenBalance (tag = this.tag, forceSender = false) {
|
|
|
|
const token = this.getToken(tag, forceSender);
|
2016-12-07 12:47:44 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
if (!token) {
|
|
|
|
return new BigNumber(0);
|
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
const value = new BigNumber(token.value || 0);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
getTokenValue (tag = this.tag, value = this.value, inverse = false) {
|
|
|
|
const token = this.getToken(tag);
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
return new BigNumber(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
const format = token.token
|
|
|
|
? new BigNumber(token.token.format || 1)
|
|
|
|
: new BigNumber(1);
|
|
|
|
|
|
|
|
let _value;
|
|
|
|
|
|
|
|
try {
|
|
|
|
_value = new BigNumber(value || 0);
|
|
|
|
} catch (error) {
|
|
|
|
_value = new BigNumber(0);
|
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
if (token.token && token.token.tag.toLowerCase() === 'eth') {
|
|
|
|
if (inverse) {
|
|
|
|
return this.api.util.fromWei(_value);
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
2017-01-10 13:26:30 +01:00
|
|
|
|
|
|
|
return this.api.util.toWei(_value);
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
if (inverse) {
|
|
|
|
return _value.div(format);
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
return _value.mul(format);
|
|
|
|
}
|
|
|
|
|
|
|
|
getValues (_gasTotal) {
|
|
|
|
const gasTotal = new BigNumber(_gasTotal || 0);
|
|
|
|
const { valueAll, isEth, isWallet } = this;
|
|
|
|
|
2017-01-11 17:01:35 +01:00
|
|
|
log.debug('@getValues', 'gas', gasTotal.toFormat());
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
if (!valueAll) {
|
|
|
|
const value = this.getTokenValue();
|
|
|
|
|
|
|
|
// If it's a token or a wallet, eth is the estimated gas,
|
|
|
|
// and value is the user input
|
|
|
|
if (!isEth || isWallet) {
|
|
|
|
return {
|
|
|
|
eth: gasTotal,
|
|
|
|
token: value
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, eth is the sum of the gas and the user input
|
|
|
|
const totalEthValue = gasTotal.plus(value);
|
|
|
|
|
|
|
|
return {
|
|
|
|
eth: totalEthValue,
|
|
|
|
token: value
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's the total balance that needs to be sent, send the total balance
|
|
|
|
// if it's not a proper ETH transfer
|
|
|
|
if (!isEth || isWallet) {
|
|
|
|
const tokenBalance = this.getTokenBalance();
|
|
|
|
|
|
|
|
return {
|
|
|
|
eth: gasTotal,
|
|
|
|
token: tokenBalance
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, substract the gas estimate
|
|
|
|
const availableEth = this.getTokenBalance('ETH');
|
|
|
|
const totalEthValue = availableEth.gt(gasTotal)
|
|
|
|
? availableEth.minus(gasTotal)
|
|
|
|
: new BigNumber(0);
|
|
|
|
|
|
|
|
return {
|
|
|
|
eth: totalEthValue.plus(gasTotal),
|
|
|
|
token: totalEthValue
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
getFormattedTokenValue (tokenValue) {
|
|
|
|
const token = this.getToken();
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
return new BigNumber(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
const tag = token.token && token.token.tag || '';
|
|
|
|
|
|
|
|
return this.getTokenValue(tag, tokenValue, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@action recalculate = (redo = false) => {
|
|
|
|
const { account } = this;
|
|
|
|
|
|
|
|
if (!account || !this.balance) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const balance = this.getBalance();
|
|
|
|
|
|
|
|
if (!balance) {
|
|
|
|
return;
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
const gasTotal = new BigNumber(this.gasStore.price || 0).mul(new BigNumber(this.gasStore.gas || 0));
|
|
|
|
|
|
|
|
const ethBalance = this.getTokenBalance('ETH', true);
|
|
|
|
const tokenBalance = this.getTokenBalance();
|
|
|
|
const { eth, token } = this.getValues(gasTotal);
|
|
|
|
|
|
|
|
let totalEth = gasTotal;
|
|
|
|
let totalError = null;
|
|
|
|
let valueError = null;
|
|
|
|
|
|
|
|
if (eth.gt(ethBalance)) {
|
2016-12-02 15:21:01 +01:00
|
|
|
totalError = ERRORS.largeAmount;
|
|
|
|
}
|
|
|
|
|
2017-01-10 13:26:30 +01:00
|
|
|
if (token && token.gt(tokenBalance)) {
|
|
|
|
valueError = ERRORS.largeAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
log.debug('@recalculate', {
|
|
|
|
eth: eth.toFormat(),
|
|
|
|
token: token.toFormat(),
|
|
|
|
ethBalance: ethBalance.toFormat(),
|
|
|
|
tokenBalance: tokenBalance.toFormat(),
|
|
|
|
gasTotal: gasTotal.toFormat()
|
|
|
|
});
|
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
transaction(() => {
|
|
|
|
this.totalError = totalError;
|
|
|
|
this.valueError = valueError;
|
2016-12-09 15:43:24 +01:00
|
|
|
this.gasStore.setErrorTotal(totalError);
|
|
|
|
this.gasStore.setEthValue(totalEth);
|
2017-01-10 13:26:30 +01:00
|
|
|
|
|
|
|
this.total = this.api.util.fromWei(eth).toFixed();
|
|
|
|
|
|
|
|
const nextValue = this.getFormattedTokenValue(token);
|
|
|
|
let prevValue;
|
|
|
|
|
|
|
|
try {
|
|
|
|
prevValue = new BigNumber(this.value || 0);
|
|
|
|
} catch (error) {
|
|
|
|
prevValue = new BigNumber(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change the input only if necessary
|
|
|
|
if (!nextValue.eq(prevValue)) {
|
|
|
|
this.value = nextValue.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re Calculate gas once more to be sure
|
|
|
|
if (redo) {
|
|
|
|
return this.recalculateGas(false);
|
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
send () {
|
|
|
|
const { options, values } = this._getTransferParams();
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2017-01-11 17:01:35 +01:00
|
|
|
log.debug('@send', 'transfer value', options.value && options.value.toFormat());
|
2016-12-23 15:31:19 +01:00
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
return this._getTransferMethod().postTransaction(options, values);
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
_estimateGas (forceToken = false) {
|
|
|
|
const { options, values } = this._getTransferParams(true, forceToken);
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
return this._getTransferMethod(true, forceToken).estimateGas(options, values);
|
|
|
|
}
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
estimateGas () {
|
2016-12-30 12:28:12 +01:00
|
|
|
return this._estimateGas();
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
_getTransferMethod (gas = false, forceToken = false) {
|
2016-12-06 09:37:59 +01:00
|
|
|
const { isEth, isWallet } = this;
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (isEth && !isWallet && !forceToken) {
|
2016-12-06 09:37:59 +01:00
|
|
|
return gas ? this.api.eth : this.api.parity;
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (isWallet && !forceToken) {
|
2016-12-06 09:37:59 +01:00
|
|
|
return this.wallet.instance.execute;
|
|
|
|
}
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
return this.token.contract.instance.transfer;
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
_getData (gas = false) {
|
|
|
|
const { isEth, isWallet } = this;
|
|
|
|
|
|
|
|
if (!isWallet || isEth) {
|
|
|
|
return this.data && this.data.length ? this.data : '';
|
|
|
|
}
|
|
|
|
|
|
|
|
const func = this._getTransferMethod(gas, true);
|
|
|
|
const { options, values } = this._getTransferParams(gas, true);
|
|
|
|
|
|
|
|
return this.token.contract.getCallData(func, options, values);
|
|
|
|
}
|
|
|
|
|
|
|
|
_getTransferParams (gas = false, forceToken = false) {
|
2016-12-06 09:37:59 +01:00
|
|
|
const { isEth, isWallet } = this;
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
const to = (isEth && !isWallet) ? this.recipient
|
|
|
|
: (this.isWallet ? this.wallet.address : this.token.address);
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-02-03 20:01:09 +01:00
|
|
|
const options = this.gasStore.overrideTransaction({
|
2016-12-06 09:37:59 +01:00
|
|
|
from: this.sender || this.account.address,
|
|
|
|
to
|
2017-02-03 20:01:09 +01:00
|
|
|
});
|
2016-12-02 15:21:01 +01:00
|
|
|
|
2017-02-03 20:01:09 +01:00
|
|
|
if (gas) {
|
2016-12-06 09:37:59 +01:00
|
|
|
options.gas = MAX_GAS_ESTIMATION;
|
|
|
|
}
|
|
|
|
|
2017-01-11 17:01:35 +01:00
|
|
|
const gasTotal = new BigNumber(options.gas || DEFAULT_GAS).mul(options.gasPrice || DEFAULT_GASPRICE);
|
|
|
|
const { token } = this.getValues(gasTotal);
|
2017-01-10 13:26:30 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (isEth && !isWallet && !forceToken) {
|
2017-01-10 13:26:30 +01:00
|
|
|
options.value = token;
|
2016-12-07 12:47:44 +01:00
|
|
|
options.data = this._getData(gas);
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
return { options, values: [] };
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
if (isWallet && !forceToken) {
|
|
|
|
const to = isEth ? this.recipient : this.token.contract.address;
|
2017-01-10 13:26:30 +01:00
|
|
|
const value = isEth ? token : new BigNumber(0);
|
2016-12-07 12:47:44 +01:00
|
|
|
|
|
|
|
const values = [
|
|
|
|
to, value,
|
|
|
|
this._getData(gas)
|
2016-12-06 09:37:59 +01:00
|
|
|
];
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
return { options, values };
|
|
|
|
}
|
|
|
|
|
|
|
|
const values = [
|
|
|
|
this.recipient,
|
2017-01-10 13:26:30 +01:00
|
|
|
token.toFixed(0)
|
2016-12-07 12:47:44 +01:00
|
|
|
];
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
return { options, values };
|
2016-12-02 15:21:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_validatePositiveNumber (num) {
|
|
|
|
try {
|
|
|
|
const v = new BigNumber(num);
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-12-02 15:21:01 +01:00
|
|
|
if (v.lt(0)) {
|
|
|
|
return ERRORS.invalidAmount;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
return ERRORS.invalidAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
_validateDecimals (num) {
|
|
|
|
const { balance } = this;
|
|
|
|
|
|
|
|
if (this.tag === 'ETH') {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const token = balance.tokens.find((balance) => balance.token.tag === this.tag).token;
|
|
|
|
const s = new BigNumber(num).mul(token.format || 1).toFixed();
|
|
|
|
|
|
|
|
if (s.indexOf('.') !== -1) {
|
|
|
|
return ERRORS.invalidDecimals;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|