Add functionalities to multi-sig wallet (#3729)
* WIP Sending tokens in multi-sig wallet * Working Token transfer for multi-sig wallet #3282 * Add operation hash to transfer modal * Add existing wallet from address #3282 * Wallet delete redirect to Wallets/Accounts #3282 * Rightly check balance in Transfer // Get all accounts balances #3282 * Fix linting * Better Header UI for Wallet * Use the `~` webpack alias * Use Webpack `~` alias
This commit is contained in:
committed by
Jaco Greeff
parent
be90245ecb
commit
8dbd56888d
@@ -239,11 +239,7 @@ export default class Details extends Component {
|
||||
}
|
||||
|
||||
renderTokenSelect () {
|
||||
const { balance, images, tag, wallet } = this.props;
|
||||
|
||||
if (wallet) {
|
||||
return null;
|
||||
}
|
||||
const { balance, images, tag } = this.props;
|
||||
|
||||
return (
|
||||
<TokenSelect
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
import { observable, computed, action, transaction } from 'mobx';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
import { wallet as walletAbi } from '~/contracts/abi';
|
||||
import { bytesToHex } from '~/api/util/format';
|
||||
import Contract from '~/api/contract';
|
||||
import ERRORS from './errors';
|
||||
import { ERROR_CODES } from '~/api/transport/error';
|
||||
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||
@@ -71,6 +75,9 @@ export default class TransferStore {
|
||||
gasLimit = null;
|
||||
onClose = null;
|
||||
|
||||
senders = null;
|
||||
sendersBalances = null;
|
||||
|
||||
isWallet = false;
|
||||
wallet = null;
|
||||
|
||||
@@ -108,19 +115,23 @@ export default class TransferStore {
|
||||
constructor (api, props) {
|
||||
this.api = api;
|
||||
|
||||
const { account, balance, gasLimit, senders, onClose } = props;
|
||||
const { account, balance, gasLimit, senders, onClose, newError, sendersBalances } = props;
|
||||
|
||||
this.account = account;
|
||||
this.balance = balance;
|
||||
this.gasLimit = gasLimit;
|
||||
this.onClose = onClose;
|
||||
this.isWallet = account && account.wallet;
|
||||
this.newError = newError;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +228,10 @@ export default class TransferStore {
|
||||
this.txhash = txhash;
|
||||
this.busyState = 'Your transaction has been posted to the network';
|
||||
});
|
||||
|
||||
if (this.isWallet) {
|
||||
return this._attachWalletOperation(txhash);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.sending = false;
|
||||
@@ -224,6 +239,34 @@ export default class TransferStore {
|
||||
});
|
||||
}
|
||||
|
||||
@action _attachWalletOperation = (txhash) => {
|
||||
let ethSubscriptionId = null;
|
||||
|
||||
return this.api.subscribe('eth_blockNumber', () => {
|
||||
this.api.eth
|
||||
.getTransactionReceipt(txhash)
|
||||
.then((tx) => {
|
||||
if (!tx) {
|
||||
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];
|
||||
}
|
||||
|
||||
this.api.unsubscribe(ethSubscriptionId);
|
||||
ethSubscriptionId = null;
|
||||
});
|
||||
}).then((subId) => {
|
||||
ethSubscriptionId = subId;
|
||||
});
|
||||
}
|
||||
|
||||
@action _onUpdateAll = (valueAll) => {
|
||||
this.valueAll = valueAll;
|
||||
this.recalculateGas();
|
||||
@@ -355,19 +398,29 @@ export default class TransferStore {
|
||||
}
|
||||
|
||||
@action recalculate = () => {
|
||||
const { account, balance } = this;
|
||||
const { account } = this;
|
||||
|
||||
if (!account || !balance) {
|
||||
if (!account || !this.balance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const balance = this.senders
|
||||
? this.sendersBalances[this.sender]
|
||||
: this.balance;
|
||||
|
||||
if (!balance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { gas, gasPrice, tag, valueAll, isEth } = this;
|
||||
|
||||
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);
|
||||
|
||||
const senderBalance = this.balance.tokens.find((b) => tag === b.token.tag);
|
||||
const available = new BigNumber(senderBalance.value);
|
||||
const format = new BigNumber(senderBalance.token.format || 1);
|
||||
|
||||
let { value, valueError } = this;
|
||||
let totalEth = gasTotal;
|
||||
@@ -409,26 +462,52 @@ export default class TransferStore {
|
||||
return this._getTransferMethod().postTransaction(options, values);
|
||||
}
|
||||
|
||||
estimateGas () {
|
||||
const { options, values } = this._getTransferParams(true);
|
||||
return this._getTransferMethod(true).estimateGas(options, values);
|
||||
_estimateGas (forceToken = false) {
|
||||
const { options, values } = this._getTransferParams(true, forceToken);
|
||||
return this._getTransferMethod(true, forceToken).estimateGas(options, values);
|
||||
}
|
||||
|
||||
_getTransferMethod (gas = false) {
|
||||
estimateGas () {
|
||||
if (this.isEth || !this.isWallet) {
|
||||
return this._estimateGas();
|
||||
}
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this._estimateGas(true),
|
||||
this._estimateGas()
|
||||
])
|
||||
.then((results) => results[0].plus(results[1]));
|
||||
}
|
||||
|
||||
_getTransferMethod (gas = false, forceToken = false) {
|
||||
const { isEth, isWallet } = this;
|
||||
|
||||
if (isEth && !isWallet) {
|
||||
if (isEth && !isWallet && !forceToken) {
|
||||
return gas ? this.api.eth : this.api.parity;
|
||||
}
|
||||
|
||||
if (isWallet) {
|
||||
if (isWallet && !forceToken) {
|
||||
return this.wallet.instance.execute;
|
||||
}
|
||||
|
||||
return this.token.contract.instance.transfer;
|
||||
}
|
||||
|
||||
_getTransferParams (gas = false) {
|
||||
_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) {
|
||||
const { isEth, isWallet } = this;
|
||||
|
||||
const to = (isEth && !isWallet) ? this.recipient
|
||||
@@ -446,27 +525,30 @@ export default class TransferStore {
|
||||
options.gas = MAX_GAS_ESTIMATION;
|
||||
}
|
||||
|
||||
if (isEth && !isWallet) {
|
||||
if (isEth && !isWallet && !forceToken) {
|
||||
options.value = this.api.util.toWei(this.value || 0);
|
||||
|
||||
if (this.data && this.data.length) {
|
||||
options.data = this.data;
|
||||
}
|
||||
options.data = this._getData(gas);
|
||||
|
||||
return { options, values: [] };
|
||||
}
|
||||
|
||||
const values = isWallet
|
||||
? [
|
||||
this.recipient,
|
||||
this.api.util.toWei(this.value || 0),
|
||||
this.data || ''
|
||||
]
|
||||
: [
|
||||
this.recipient,
|
||||
new BigNumber(this.value || 0).mul(this.token.format).toFixed(0)
|
||||
if (isWallet && !forceToken) {
|
||||
const to = isEth ? this.recipient : this.token.contract.address;
|
||||
const value = isEth ? this.api.util.toWei(this.value || 0) : new BigNumber(0);
|
||||
|
||||
const values = [
|
||||
to, value,
|
||||
this._getData(gas)
|
||||
];
|
||||
|
||||
return { options, values };
|
||||
}
|
||||
|
||||
const values = [
|
||||
this.recipient,
|
||||
new BigNumber(this.value || 0).mul(this.token.format).toFixed(0)
|
||||
];
|
||||
|
||||
return { options, values };
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { observer } from 'mobx-react';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
@@ -25,7 +26,7 @@ import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
|
||||
import { newError } from '~/ui/Errors/actions';
|
||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui';
|
||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash, Input } from '~/ui';
|
||||
import { nullableProptype } from '~/util/proptypes';
|
||||
|
||||
import Details from './Details';
|
||||
@@ -45,10 +46,10 @@ class Transfer extends Component {
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
images: PropTypes.object.isRequired,
|
||||
|
||||
account: PropTypes.object,
|
||||
senders: nullableProptype(PropTypes.object),
|
||||
sendersBalances: nullableProptype(PropTypes.object),
|
||||
account: PropTypes.object,
|
||||
balance: PropTypes.object,
|
||||
balances: PropTypes.object,
|
||||
wallet: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
@@ -133,6 +134,25 @@ class Transfer extends Component {
|
||||
return (
|
||||
<CompletedStep>
|
||||
<TxHash hash={ txhash } />
|
||||
{
|
||||
this.store.operation
|
||||
? (
|
||||
<div>
|
||||
<br />
|
||||
<p>
|
||||
This transaction needs confirmation from other owners.
|
||||
<Input
|
||||
style={ { width: '50%', margin: '0 auto' } }
|
||||
value={ this.store.operation }
|
||||
label='operation hash'
|
||||
readOnly
|
||||
allowCopy
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</CompletedStep>
|
||||
);
|
||||
}
|
||||
@@ -277,7 +297,9 @@ function mapStateToProps (initState, initProps) {
|
||||
|
||||
return (state) => {
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
return { gasLimit, wallet, senders };
|
||||
const sendersBalances = senders ? pick(state.balances.balances, Object.keys(senders)) : null;
|
||||
|
||||
return { gasLimit, wallet, senders, sendersBalances };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user