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

Make Wallet first-class citizens #3990

Merged
merged 28 commits into from
Dec 30, 2016
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f5573df
Fixed hint in Address Select + Wallet as first-class-citizen
ngotchac Dec 27, 2016
aee74ce
Separate Owned and not Owned Wallets
ngotchac Dec 27, 2016
f3bf753
Fix balance not updating
ngotchac Dec 27, 2016
af23c58
Merge branch 'master' into ng-wallet-improv
ngotchac Dec 27, 2016
1b859bb
Fix MethodDecoding for Contract Deployment
ngotchac Dec 27, 2016
cb2bef6
Fix TypedInput params
ngotchac Dec 27, 2016
1234364
Fix Token Transfer for Wallet
ngotchac Dec 27, 2016
1c70fe7
Small change to contracts
ngotchac Dec 27, 2016
e0232ca
Fix wallets shown twice
ngotchac Dec 27, 2016
03dd6b8
Fix separation of accounts and wallets in Accounts
ngotchac Dec 28, 2016
fe1e816
Fix linting
ngotchac Dec 28, 2016
f371854
Execute contract methods from Wallet ✓
ngotchac Dec 28, 2016
a32bb86
Fixing linting
ngotchac Dec 28, 2016
17cb600
Wallet as first-class citizen: Part 1 (Manual) #3784
ngotchac Dec 28, 2016
ea73917
Lower level wallet transaction convertion
ngotchac Dec 28, 2016
c307fcc
Fix linting
ngotchac Dec 28, 2016
28e2ce5
Proper autoFocus on right Signer input
ngotchac Dec 29, 2016
eab686f
PR Grumble: don't show Wallets in dApps Permissions
ngotchac Dec 29, 2016
84f7b79
Add postTransaction and gasEstimate wrapper methods
ngotchac Dec 29, 2016
69e484b
Extract Wallet postTx and gasEstimate to utils + PATCH api
ngotchac Dec 29, 2016
234919d
Remove invalid test
ngotchac Dec 29, 2016
7a1acd7
Merge branch 'master' into ng-wallet-improv
ngotchac Dec 29, 2016
b191961
Merge master
ngotchac Dec 29, 2016
fb5d45b
Fix linting
ngotchac Dec 29, 2016
6895afe
Merge branch 'ng-wallet-improv' of github.com:ethcore/parity into ng-…
ngotchac Dec 29, 2016
1fdf8f6
Fix merge issue
ngotchac Dec 29, 2016
e4f782c
Rename Portal
ngotchac Dec 29, 2016
f087475
Rename Protal => Portal (typo)
ngotchac Dec 29, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion js/src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ export default class Api {
}
})
.catch((error) => {
console.error('pollMethod', error);
// Don't print if the request is rejected: that's ok
if (error.type !== 'REQUEST_REJECTED') {
console.error('pollMethod', error);
}

reject(error);
});
};
Expand Down
133 changes: 118 additions & 15 deletions js/src/api/contract/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

import Abi from '../../abi';
import BigNumber from 'bignumber.js';
import { intersection } from 'lodash';

import Abi from '~/abi';
import WalletAbi from '~/contracts/abi/wallet.json';
import WalletsUtils from '~/util/wallets';
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed, these two lines above are UI and implementation specific, and doesn't belong in the API. The API layer takes what is provided and encodes that, sends it, decodes the responses, the role is not to make decisions and tweaks on behalf of the caller (caller is either us or other developers).

Changes made here is valid for the app, just needs a different home outside of ~/api


const _cachedWalletLookup = {};
let nextSubscriptionId = 0;

export default class Contract {
Expand Down Expand Up @@ -90,8 +96,11 @@ export default class Contract {
}

deployEstimateGas (options, values) {
return this._api.eth
.estimateGas(this._encodeOptions(this.constructors[0], options, values))
return this
._encodeOptions(this.constructors[0], options, values, false)
.then((_options) => {
return this._api.eth.estimateGas(_options);
})
.then((gasEst) => {
return [gasEst, gasEst.mul(1.2)];
});
Expand All @@ -115,8 +124,11 @@ export default class Contract {

setState({ state: 'postTransaction', gas });

return this._api.parity
.postTransaction(this._encodeOptions(this.constructors[0], options, values))
return this
._encodeOptions(this.constructors[0], options, values, false)
.then((_options) => {
return this._api.parity.postTransaction(_options);
})
.then((requestId) => {
setState({ state: 'checkRequest', requestId });
return this._pollCheckRequest(requestId);
Expand Down Expand Up @@ -209,9 +221,93 @@ export default class Contract {
return `0x${data || ''}${call || ''}`;
}

_encodeOptions (func, options, values) {
/**
* Check whether the given address could be
* a Wallet. The result is cached in order not
* to make unnecessary calls on non wallet accounts
*/
_isWallet (address) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not 100% sure this is the correct place for this functionality. Can we walk through today and discuss?

if (!_cachedWalletLookup[address]) {
const walletContract = new Contract(this._api, WalletAbi);

_cachedWalletLookup[address] = walletContract
.at(address)
.instance
.m_numOwners
.call()
.then((result) => {
if (!result || result.equals(0)) {
return false;
}

return true;
})
.then((bool) => {
_cachedWalletLookup[address] = Promise.resolve(bool);
return bool;
});
}

return _cachedWalletLookup[address];
}

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

if (!tryWallet || !options.from) {
return Promise.resolve(options);
}

// Try to find out if the `from` field is a Wallet
// And if one of the owners is in the accounts
return this
._isWallet(options.from)
.then((isWallet) => {
if (!isWallet) {
return options;
}

const walletContract = new Contract(this._api, WalletAbi);

const promises = [
this._api.parity.accountsInfo(),
WalletsUtils.fetchOwners(walletContract.at(options.from))
];

return Promise
.all(promises)
.then(([ accounts, owners ]) => {
const addresses = Object.keys(accounts);
const owner = intersection(addresses, owners).pop();

if (!owner) {
return false;
}

return owner;
})
.then((owner) => {
if (!owner) {
return options;
}

const _options = Object.assign({}, options);
const { from, to, value = new BigNumber(0), data } = options;

delete _options.data;

const nextValues = [ to, value, data ];
const nextOptions = {
..._options,
from: owner,
to: from
};

const execFunc = walletContract.instance.execute;

return this._encodeOptions(execFunc, nextOptions, nextValues, tryWallet);
});
});
}

_addOptionsTo (options = {}) {
Expand All @@ -222,24 +318,31 @@ export default class Contract {

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

return this._api.eth
.call(callParams)
return this
._encodeOptions(func, this._addOptionsTo(options), values, false)
.then((callParams) => {
return this._api.eth.call(callParams);
})
.then((encoded) => func.decodeOutput(encoded))
.then((tokens) => tokens.map((token) => token.value))
.then((returns) => returns.length === 1 ? returns[0] : returns);
};

if (!func.constant) {
func.postTransaction = (options, values = []) => {
return this._api.parity
.postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values));
return this
._encodeOptions(func, this._addOptionsTo(options), values)
.then((_options) => {
return this._api.parity.postTransaction(_options);
});
};

func.estimateGas = (options, values = []) => {
return this._api.eth
.estimateGas(this._encodeOptions(func, this._addOptionsTo(options), values));
return this
._encodeOptions(func, this._addOptionsTo(options), values)
.then((_options) => {
return this._api.eth.estimateGas(_options);
});
};
}

Expand Down
5 changes: 4 additions & 1 deletion js/src/api/transport/ws/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ export default class Ws extends JsonRpcBase {
if (result.error) {
this.error(event.data);

console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
// Don't print error if request rejected...
if (!/rejected/.test(result.error.message)) {
console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
}

const error = new TransportError(method, result.error.code, result.error.message);
reject(error);
Expand Down
6 changes: 5 additions & 1 deletion js/src/modals/CreateWallet/WalletDetails/walletDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

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

import { Form, TypedInput, Input, AddressSelect, InputAddress } from '~/ui';

Expand Down Expand Up @@ -73,6 +74,9 @@ export default class WalletDetails extends Component {
renderMultisigDetails () {
const { accounts, wallet, errors } = this.props;

// Wallets cannot create contracts
const _accounts = omitBy(accounts, (a) => a.wallet);

return (
<Form>
<AddressSelect
Expand All @@ -81,7 +85,7 @@ export default class WalletDetails extends Component {
value={ wallet.account }
error={ errors.account }
onChange={ this.onAccoutChange }
accounts={ accounts }
accounts={ _accounts }
/>

<Input
Expand Down
10 changes: 8 additions & 2 deletions js/src/modals/DeployContract/deployContract.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

import { pick } from 'lodash';
import { pick, omitBy } from 'lodash';
import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
Expand Down Expand Up @@ -552,13 +552,19 @@ class DeployContract extends Component {
}

function mapStateToProps (initState, initProps) {
const fromAddresses = Object.keys(initProps.accounts);
const { accounts } = initProps;

// Skip Wallet accounts : they can't create Contracts
const _accounts = omitBy(accounts, (a) => a.wallet);

const fromAddresses = Object.keys(_accounts);

return (state) => {
const balances = pick(state.balances.balances, fromAddresses);
const { gasLimit } = state.nodeStatus;

return {
accounts: _accounts,
balances,
gasLimit
};
Expand Down
1 change: 1 addition & 0 deletions js/src/modals/ExecuteContract/executeContract.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ class ExecuteContract extends Component {
const { advancedOptions, amount, func, minBlock, values } = this.state;
const steps = advancedOptions ? STAGES_ADVANCED : STAGES_BASIC;
const finalstep = steps.length - 1;

const options = {
gas: this.gasStore.gas,
gasPrice: this.gasStore.price,
Expand Down
16 changes: 2 additions & 14 deletions js/src/modals/Transfer/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,7 @@ export default class TransferStore {

const senderBalance = this.balance.tokens.find((b) => tag === b.token.tag);
const format = new BigNumber(senderBalance.token.format || 1);
const available = isWallet
? this.api.util.fromWei(new BigNumber(senderBalance.value))
: (new BigNumber(senderBalance.value)).div(format);
const available = new BigNumber(senderBalance.value).div(format);

let { value, valueError } = this;
let totalEth = gasTotal;
Expand Down Expand Up @@ -428,7 +426,6 @@ export default class TransferStore {

send () {
const { options, values } = this._getTransferParams();

options.minBlock = new BigNumber(this.minBlock || 0).gt(0) ? this.minBlock : null;

return this._getTransferMethod().postTransaction(options, values);
Expand All @@ -440,16 +437,7 @@ export default class TransferStore {
}

estimateGas () {
if (this.isEth || !this.isWallet) {
return this._estimateGas();
}

return Promise
.all([
this._estimateGas(true),
this._estimateGas()
])
.then((results) => results[0].plus(results[1]));
return this._estimateGas();
}

_getTransferMethod (gas = false, forceToken = false) {
Expand Down
14 changes: 7 additions & 7 deletions js/src/modals/WalletSettings/walletSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class WalletSettings extends Component {
};

static propTypes = {
accounts: PropTypes.object.isRequired,
accountsInfo: PropTypes.object.isRequired,
wallet: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
senders: PropTypes.object.isRequired
Expand Down Expand Up @@ -113,7 +113,7 @@ class WalletSettings extends Component {
default:
case 'EDIT':
const { wallet, errors } = this.store;
const { accounts, senders } = this.props;
const { accountsInfo, senders } = this.props;

return (
<Form>
Expand All @@ -137,7 +137,7 @@ class WalletSettings extends Component {
label='other wallet owners'
value={ wallet.owners.slice() }
onChange={ this.store.onOwnersChange }
accounts={ accounts }
accounts={ accountsInfo }
param='address[]'
/>

Expand Down Expand Up @@ -190,7 +190,7 @@ class WalletSettings extends Component {
}

renderChange (change) {
const { accounts } = this.props;
const { accountsInfo } = this.props;

switch (change.type) {
case 'dailylimit':
Expand Down Expand Up @@ -229,7 +229,7 @@ class WalletSettings extends Component {
<InputAddress
disabled
value={ change.value }
accounts={ accounts }
accounts={ accountsInfo }
/>
</div>
</div>
Expand All @@ -243,7 +243,7 @@ class WalletSettings extends Component {
<InputAddress
disabled
value={ change.value }
accounts={ accounts }
accounts={ accountsInfo }
/>
</div>
</div>
Expand Down Expand Up @@ -329,7 +329,7 @@ function mapStateToProps (initState, initProps) {
const senders = pick(accounts, owners);

return () => {
return { accounts: accountsInfo, senders };
return { accountsInfo, senders };
};
}

Expand Down
2 changes: 2 additions & 0 deletions js/src/modals/WalletSettings/walletSettingsStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const STEPS = {
};

export default class WalletSettingsStore {
accounts = {};

@observable step = null;
@observable requests = [];
@observable deployState = '';
Expand Down
4 changes: 2 additions & 2 deletions js/src/redux/providers/balancesActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,15 @@ export function fetchBalances (_addresses) {
const { api, personal } = getState();
const { visibleAccounts, accounts } = personal;

const addresses = uniq(_addresses || visibleAccounts || []);
const addresses = uniq((_addresses || visibleAccounts || []).concat(Object.keys(accounts)));

if (addresses.length === 0) {
return Promise.resolve();
}

const fullFetch = addresses.length === 1;

const addressesToFetch = uniq(addresses.concat(Object.keys(accounts)));
const addressesToFetch = uniq(addresses);

return Promise
.all(addressesToFetch.map((addr) => fetchAccount(addr, api, fullFetch)))
Expand Down
Loading