2016-12-06 09:37:59 +01:00
|
|
|
// 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 { observable, computed, action, transaction } from 'mobx';
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
import Contract from '~/api/contract';
|
2016-12-09 16:24:11 +01:00
|
|
|
import Contracts from '~/contracts';
|
2016-12-09 13:44:10 +01:00
|
|
|
import { ERROR_CODES } from '~/api/transport/error';
|
2016-12-07 12:47:44 +01:00
|
|
|
import { wallet as walletAbi } from '~/contracts/abi';
|
2016-12-09 16:55:43 +01:00
|
|
|
import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
2016-12-07 12:47:44 +01:00
|
|
|
|
2016-12-09 13:44:10 +01:00
|
|
|
import { validateUint, validateAddress, validateName } from '~/util/validation';
|
2016-12-10 01:26:28 +01:00
|
|
|
import { toWei } from '~/api/util/wei';
|
2016-12-07 12:47:44 +01:00
|
|
|
import WalletsUtils from '~/util/wallets';
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
const STEPS = {
|
2016-12-07 12:47:44 +01:00
|
|
|
TYPE: { title: 'wallet type' },
|
2016-12-06 09:37:59 +01:00
|
|
|
DETAILS: { title: 'wallet details' },
|
|
|
|
DEPLOYMENT: { title: 'wallet deployment', waiting: true },
|
|
|
|
INFO: { title: 'wallet informaton' }
|
|
|
|
};
|
|
|
|
|
|
|
|
export default class CreateWalletStore {
|
|
|
|
@observable step = null;
|
|
|
|
@observable rejected = false;
|
|
|
|
|
|
|
|
@observable deployState = null;
|
|
|
|
@observable deployError = null;
|
2016-12-07 12:47:44 +01:00
|
|
|
@observable deployed = false;
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
@observable txhash = null;
|
|
|
|
|
|
|
|
@observable wallet = {
|
|
|
|
account: '',
|
|
|
|
address: '',
|
|
|
|
owners: [],
|
|
|
|
required: 1,
|
2016-12-10 01:26:28 +01:00
|
|
|
daylimit: toWei(1),
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
name: '',
|
|
|
|
description: ''
|
|
|
|
};
|
2016-12-07 12:47:44 +01:00
|
|
|
@observable walletType = 'MULTISIG';
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
@observable errors = {
|
|
|
|
account: null,
|
2016-12-07 12:47:44 +01:00
|
|
|
address: null,
|
2016-12-06 09:37:59 +01:00
|
|
|
owners: null,
|
|
|
|
required: null,
|
|
|
|
daylimit: null,
|
2016-12-07 12:47:44 +01:00
|
|
|
name: null
|
2016-12-06 09:37:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
@computed get stage () {
|
2016-12-07 12:47:44 +01:00
|
|
|
return this.stepsKeys.findIndex((k) => k === this.step);
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed get hasErrors () {
|
2016-12-07 12:47:44 +01:00
|
|
|
return !!Object.keys(this.errors)
|
|
|
|
.filter((errorKey) => {
|
|
|
|
if (this.walletType === 'WATCH') {
|
|
|
|
return ['address', 'name'].includes(errorKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return errorKey !== 'address';
|
|
|
|
})
|
|
|
|
.find((key) => !!this.errors[key]);
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
@computed get stepsKeys () {
|
|
|
|
return this.steps.map((s) => s.key);
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get steps () {
|
|
|
|
return Object
|
|
|
|
.keys(STEPS)
|
|
|
|
.map((key) => {
|
|
|
|
return {
|
|
|
|
...STEPS[key],
|
|
|
|
key
|
|
|
|
};
|
|
|
|
})
|
|
|
|
.filter((step) => {
|
|
|
|
return (this.walletType !== 'WATCH' || step.key !== 'DEPLOYMENT');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get waiting () {
|
|
|
|
this.steps
|
|
|
|
.map((s, idx) => ({ idx, waiting: s.waiting }))
|
|
|
|
.filter((s) => s.waiting)
|
|
|
|
.map((s) => s.idx);
|
|
|
|
}
|
2016-12-06 09:37:59 +01:00
|
|
|
|
|
|
|
constructor (api, accounts) {
|
|
|
|
this.api = api;
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
this.step = this.stepsKeys[0];
|
2016-12-06 09:37:59 +01:00
|
|
|
this.wallet.account = Object.values(accounts)[0].address;
|
2016-12-07 12:47:44 +01:00
|
|
|
this.validateWallet(this.wallet);
|
|
|
|
}
|
|
|
|
|
|
|
|
@action onTypeChange = (type) => {
|
|
|
|
this.walletType = type;
|
|
|
|
this.validateWallet(this.wallet);
|
|
|
|
}
|
|
|
|
|
|
|
|
@action onNext = () => {
|
|
|
|
const stepIndex = this.stepsKeys.findIndex((k) => k === this.step) + 1;
|
|
|
|
this.step = this.stepsKeys[stepIndex];
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action onChange = (_wallet) => {
|
|
|
|
const newWallet = Object.assign({}, this.wallet, _wallet);
|
2016-12-07 12:47:44 +01:00
|
|
|
this.validateWallet(newWallet);
|
|
|
|
}
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
@action onAdd = () => {
|
|
|
|
if (this.hasErrors) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const walletContract = new Contract(this.api, walletAbi).at(this.wallet.address);
|
|
|
|
|
|
|
|
return Promise
|
|
|
|
.all([
|
|
|
|
WalletsUtils.fetchRequire(walletContract),
|
|
|
|
WalletsUtils.fetchOwners(walletContract),
|
|
|
|
WalletsUtils.fetchDailylimit(walletContract)
|
|
|
|
])
|
|
|
|
.then(([ require, owners, dailylimit ]) => {
|
|
|
|
transaction(() => {
|
|
|
|
this.wallet.owners = owners;
|
|
|
|
this.wallet.required = require.toNumber();
|
|
|
|
this.wallet.dailylimit = dailylimit.limit;
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.addWallet(this.wallet);
|
|
|
|
});
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action onCreate = () => {
|
|
|
|
if (this.hasErrors) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.step = 'DEPLOYMENT';
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
const { account, owners, required, daylimit } = this.wallet;
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-09 16:24:11 +01:00
|
|
|
Contracts
|
|
|
|
.get()
|
|
|
|
.registry
|
|
|
|
.lookupAddress(walletLibraryRegKey)
|
|
|
|
.then((address) => {
|
2016-12-09 16:29:57 +01:00
|
|
|
const walletLibraryAddress = (address || '').replace(/^0x/, '').toLowerCase();
|
|
|
|
const code = walletLibraryAddress.length && !/^0+$/.test(walletLibraryAddress)
|
|
|
|
? walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress)
|
|
|
|
: fullWalletCode;
|
2016-12-09 16:24:11 +01:00
|
|
|
|
|
|
|
const options = {
|
|
|
|
data: code,
|
|
|
|
from: account
|
|
|
|
};
|
2016-12-06 09:37:59 +01:00
|
|
|
|
2016-12-09 16:24:11 +01:00
|
|
|
return this.api
|
|
|
|
.newContract(walletAbi)
|
|
|
|
.deploy(options, [ owners, required, daylimit ], this.onDeploymentState);
|
|
|
|
})
|
2016-12-06 09:37:59 +01:00
|
|
|
.then((address) => {
|
2016-12-07 12:47:44 +01:00
|
|
|
this.deployed = true;
|
|
|
|
this.wallet.address = address;
|
|
|
|
return this.addWallet(this.wallet);
|
2016-12-06 09:37:59 +01:00
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
|
|
|
|
this.rejected = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.error('error deploying contract', error);
|
|
|
|
this.deployError = error;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
@action addWallet = (wallet) => {
|
|
|
|
const { address, name, description } = wallet;
|
|
|
|
|
|
|
|
return Promise
|
|
|
|
.all([
|
|
|
|
this.api.parity.setAccountName(address, name),
|
|
|
|
this.api.parity.setAccountMeta(address, {
|
|
|
|
abi: walletAbi,
|
|
|
|
wallet: true,
|
|
|
|
timestamp: Date.now(),
|
|
|
|
deleted: false,
|
|
|
|
description,
|
|
|
|
name,
|
|
|
|
tags: ['wallet']
|
|
|
|
})
|
|
|
|
])
|
|
|
|
.then(() => {
|
|
|
|
this.step = 'INFO';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-06 09:37:59 +01:00
|
|
|
onDeploymentState = (error, data) => {
|
|
|
|
if (error) {
|
|
|
|
return console.error('createWallet::onDeploymentState', error);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (data.state) {
|
|
|
|
case 'estimateGas':
|
|
|
|
case 'postTransaction':
|
|
|
|
this.deployState = 'Preparing transaction for network transmission';
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'checkRequest':
|
|
|
|
this.deployState = 'Waiting for confirmation of the transaction in the Parity Secure Signer';
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'getTransactionReceipt':
|
|
|
|
this.deployState = 'Waiting for the contract deployment transaction receipt';
|
|
|
|
this.txhash = data.txhash;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'hasReceipt':
|
|
|
|
case 'getCode':
|
|
|
|
this.deployState = 'Validating the deployed contract code';
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'completed':
|
|
|
|
this.deployState = 'The contract deployment has been completed';
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
console.error('createWallet::onDeploymentState', 'unknow contract deployment state', data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
@action validateWallet = (_wallet) => {
|
|
|
|
const addressValidation = validateAddress(_wallet.address);
|
2016-12-06 09:37:59 +01:00
|
|
|
const accountValidation = validateAddress(_wallet.account);
|
|
|
|
const requiredValidation = validateUint(_wallet.required);
|
|
|
|
const daylimitValidation = validateUint(_wallet.daylimit);
|
|
|
|
const nameValidation = validateName(_wallet.name);
|
|
|
|
|
|
|
|
const errors = {
|
2016-12-07 12:47:44 +01:00
|
|
|
address: addressValidation.addressError,
|
2016-12-06 09:37:59 +01:00
|
|
|
account: accountValidation.addressError,
|
|
|
|
required: requiredValidation.valueError,
|
|
|
|
daylimit: daylimitValidation.valueError,
|
|
|
|
name: nameValidation.nameError
|
|
|
|
};
|
|
|
|
|
|
|
|
const wallet = {
|
|
|
|
..._wallet,
|
2016-12-07 12:47:44 +01:00
|
|
|
address: addressValidation.address,
|
2016-12-06 09:37:59 +01:00
|
|
|
account: accountValidation.address,
|
|
|
|
required: requiredValidation.value,
|
|
|
|
daylimit: daylimitValidation.value,
|
|
|
|
name: nameValidation.name
|
|
|
|
};
|
|
|
|
|
2016-12-07 12:47:44 +01:00
|
|
|
transaction(() => {
|
|
|
|
this.wallet = wallet;
|
|
|
|
this.errors = errors;
|
|
|
|
});
|
2016-12-06 09:37:59 +01:00
|
|
|
}
|
|
|
|
}
|