Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Add functionalities to multi-sig wallet #3729

Merged
merged 11 commits into from
Dec 7, 2016
18 changes: 12 additions & 6 deletions js/src/api/contract/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,21 @@ export default class Contract {
});
}

_encodeOptions (func, options, values) {
getCallData = (func, options, values) => {
let data = options.data;

const tokens = func ? this._abi.encodeTokens(func.inputParamTypes(), values) : null;
const call = tokens ? func.encodeCall(tokens) : null;

if (options.data && options.data.substr(0, 2) === '0x') {
options.data = options.data.substr(2);
if (data && data.substr(0, 2) === '0x') {
data = data.substr(2);
}
options.data = `0x${options.data || ''}${call || ''}`;

return `0x${data || ''}${call || ''}`;
}

_encodeOptions (func, options, values) {
options.data = this.getCallData(func, options, values);
return options;
}

Expand All @@ -209,10 +215,10 @@ export default class Contract {

_bindFunction = (func) => {
func.call = (options, values = []) => {
const callData = this._encodeOptions(func, this._addOptionsTo(options), values);
const callParams = this._encodeOptions(func, this._addOptionsTo(options), values);

return this._api.eth
.call(callData)
.call(callParams)
.then((encoded) => func.decodeOutput(encoded))
.then((tokens) => tokens.map((token) => token.value))
.then((returns) => returns.length === 1 ? returns[0] : returns);
Expand Down
91 changes: 71 additions & 20 deletions js/src/modals/CreateWallet/WalletDetails/walletDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,62 @@

import React, { Component, PropTypes } from 'react';

import { Form, TypedInput, Input, AddressSelect } from '../../../ui';
import { parseAbiType } from '../../../util/abi';
import { Form, TypedInput, Input, AddressSelect, InputAddress } from '~/ui';
import { parseAbiType } from '~/util/abi';

import styles from '../createWallet.css';

export default class WalletDetails extends Component {
static propTypes = {
accounts: PropTypes.object.isRequired,
wallet: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired,
walletType: PropTypes.string.isRequired
};

render () {
const { walletType } = this.props;

if (walletType === 'WATCH') {
return this.renderWatchDetails();
}

return this.renderMultisigDetails();
}

renderWatchDetails () {
const { wallet, errors } = this.props;

return (
<Form>
<InputAddress
label='wallet address'
hint='the wallet contract address'
value={ wallet.address }
error={ errors.address }
onChange={ this.onAddressChange }
/>

<Input
label='wallet name'
hint='the local name for this wallet'
value={ wallet.name }
error={ errors.name }
onChange={ this.onNameChange }
/>

<Input
label='wallet description (optional)'
hint='the local description for this wallet'
value={ wallet.description }
onChange={ this.onDescriptionChange }
/>
</Form>
);
}

renderMultisigDetails () {
const { accounts, wallet, errors } = this.props;

return (
Expand Down Expand Up @@ -64,27 +108,34 @@ export default class WalletDetails extends Component {
param={ parseAbiType('address[]') }
/>

<TypedInput
label='required owners'
hint='number of required owners to accept a transaction'
value={ wallet.required }
error={ errors.required }
onChange={ this.onRequiredChange }
param={ parseAbiType('uint') }
/>

<TypedInput
label='wallet day limit'
hint='number of days to wait for other owners confirmation'
value={ wallet.daylimit }
error={ errors.daylimit }
onChange={ this.onDaylimitChange }
param={ parseAbiType('uint') }
/>
<div className={ styles.splitInput }>
<TypedInput
label='required owners'
hint='number of required owners to accept a transaction'
value={ wallet.required }
error={ errors.required }
onChange={ this.onRequiredChange }
param={ parseAbiType('uint') }
min={ 1 }
/>

<TypedInput
label='wallet day limit'
hint='number of days to wait for other owners confirmation'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the hint correct?

value={ wallet.daylimit }
error={ errors.daylimit }
onChange={ this.onDaylimitChange }
param={ parseAbiType('uint') }
/>
</div>
</Form>
);
}

onAddressChange = (_, address) => {
this.props.onChange({ address });
}

onAccoutChange = (_, account) => {
this.props.onChange({ account });
}
Expand Down
16 changes: 11 additions & 5 deletions js/src/modals/CreateWallet/WalletInfo/walletInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ export default class WalletInfo extends Component {
daylimit: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]).isRequired
]).isRequired,

deployed: PropTypes.bool
};

render () {
const { address, required, daylimit, name } = this.props;
const { address, required, daylimit, name, deployed } = this.props;

return (
<CompletedStep>
<div><code>{ name }</code> has been deployed at</div>
<div>
<code>{ name }</code>
<span> has been </span>
<span> { deployed ? 'deployed' : 'added' } at </span>
</div>
<div>
<CopyToClipboard data={ address } label='copy address to clipboard' />
<IdentityIcon address={ address } inline center className={ styles.identityicon } />
Expand All @@ -63,9 +69,9 @@ export default class WalletInfo extends Component {
}

renderOwners () {
const { account, owners } = this.props;
const { account, owners, deployed } = this.props;

return [].concat(account, owners).map((address, id) => (
return [].concat(deployed ? account : null, owners).filter((a) => a).map((address, id) => (
<div key={ id } className={ styles.owner }>
<IdentityIcon address={ address } inline center className={ styles.identityicon } />
<div className={ styles.address }>{ this.addressToString(address) }</div>
Expand Down
17 changes: 17 additions & 0 deletions js/src/modals/CreateWallet/WalletType/index.js
Original file line number Diff line number Diff line change
@@ -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 './walletType.js';
58 changes: 58 additions & 0 deletions js/src/modals/CreateWallet/WalletType/walletType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 { RadioButtons } from '~/ui';

// import styles from '../createWallet.css';

export default class WalletType extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
type: PropTypes.string.isRequired
};

render () {
const { type } = this.props;

return (
<RadioButtons
name='contractType'
value={ type }
values={ this.getTypes() }
onChange={ this.onTypeChange }
/>
);
}

getTypes () {
return [
{
label: 'Multi-Sig wallet', key: 'MULTISIG',
description: 'A standard multi-signature Wallet'
},
{
label: 'Watch a wallet', key: 'WATCH',
description: 'Add an existing wallet to your accounts'
}
];
}

onTypeChange = (type) => {
this.props.onChange(type.key);
}
}
19 changes: 19 additions & 0 deletions js/src/modals/CreateWallet/createWallet.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,22 @@
height: 24px;
}
}

.splitInput {
display: flex;
flex-direction: row;

> * {
flex: 1;

margin: 0 0.25em;

&:first-child {
margin-left: 0;
}

&:last-child {
margin-right: 0;
}
}
}
50 changes: 41 additions & 9 deletions js/src/modals/CreateWallet/createWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forwa

import { Button, Modal, TxHash, BusyStep } from '../../ui';

import WalletType from './WalletType';
import WalletDetails from './WalletDetails';
import WalletInfo from './WalletInfo';
import CreateWalletStore from './createWalletStore';
Expand Down Expand Up @@ -64,7 +65,7 @@ export default class CreateWallet extends Component {
visible
actions={ this.renderDialogActions() }
current={ stage }
steps={ steps }
steps={ steps.map((s) => s.title) }
waiting={ waiting }
>
{ this.renderPage() }
Expand Down Expand Up @@ -98,24 +99,35 @@ export default class CreateWallet extends Component {
required={ this.store.wallet.required }
daylimit={ this.store.wallet.daylimit }
name={ this.store.wallet.name }

deployed={ this.store.deployed }
/>
);

default:
case 'DETAILS':
return (
<WalletDetails
accounts={ accounts }
wallet={ this.store.wallet }
errors={ this.store.errors }
walletType={ this.store.walletType }
onChange={ this.store.onChange }
/>
);

default:
case 'TYPE':
return (
<WalletType
onChange={ this.store.onTypeChange }
type={ this.store.walletType }
/>
);
}
}

renderDialogActions () {
const { step, hasErrors, rejected, onCreate } = this.store;
const { step, hasErrors, rejected, onCreate, onNext, onAdd } = this.store;

const cancelBtn = (
<Button
Expand Down Expand Up @@ -149,12 +161,11 @@ export default class CreateWallet extends Component {
/>
);

const createBtn = (
const nextBtn = (
<Button
icon={ <NavigationArrowForward /> }
label='Create'
disabled={ hasErrors }
onClick={ onCreate }
label='Next'
onClick={ onNext }
/>
);

Expand All @@ -169,9 +180,30 @@ export default class CreateWallet extends Component {
case 'INFO':
return [ doneBtn ];

default:
case 'DETAILS':
return [ cancelBtn, createBtn ];
if (this.store.walletType === 'WATCH') {
return [ cancelBtn, (
<Button
icon={ <NavigationArrowForward /> }
label='Add'
disabled={ hasErrors }
onClick={ onAdd }
/>
) ];
}

return [ cancelBtn, (
<Button
icon={ <NavigationArrowForward /> }
label='Create'
disabled={ hasErrors }
onClick={ onCreate }
/>
) ];

default:
case 'TYPE':
return [ cancelBtn, nextBtn ];

}
}
Expand Down
Loading