From 151b9241dfb4b6255fa430a4d51eaafce663ec8e Mon Sep 17 00:00:00 2001 From: meriadec Date: Sun, 28 Oct 2018 18:00:45 +0100 Subject: [PATCH] Ability to import multiple xpubs at once --- .../DevToolsPage/AccountImporter.js | 302 +++++++++++------- src/helpers/libcore.js | 4 +- 2 files changed, 189 insertions(+), 117 deletions(-) diff --git a/src/components/DevToolsPage/AccountImporter.js b/src/components/DevToolsPage/AccountImporter.js index 9bccb67f..161585aa 100644 --- a/src/components/DevToolsPage/AccountImporter.js +++ b/src/components/DevToolsPage/AccountImporter.js @@ -1,5 +1,7 @@ // @flow +/* eslint-disable react/no-multi-comp */ + import React, { PureComponent, Fragment } from 'react' import invariant from 'invariant' import { connect } from 'react-redux' @@ -9,7 +11,8 @@ import type { Currency, Account } from '@ledgerhq/live-common/lib/types' import { decodeAccount } from 'reducers/accounts' import { addAccount } from 'actions/accounts' -import FormattedVal from 'components/base/FormattedVal' +import FakeLink from 'components/base/FakeLink' +import Ellipsis from 'components/base/Ellipsis' import Switch from 'components/base/Switch' import Spinner from 'components/base/Spinner' import Box, { Card } from 'components/base/Box' @@ -32,26 +35,40 @@ type Props = { addAccount: Account => void, } -const INITIAL_STATE = { - status: 'idle', - currency: null, - xpub: '', - account: null, - isSegwit: true, - isUnsplit: false, - error: null, +type ImportableAccountType = { + name: string, + currency: Currency, + derivationMode: string, + xpub: string, } type State = { status: string, + + importableAccounts: ImportableAccountType[], + currency: ?Currency, xpub: string, - account: ?Account, + name: string, isSegwit: boolean, isUnsplit: boolean, + error: ?Error, } +const INITIAL_STATE = { + status: 'idle', + + currency: null, + xpub: '', + name: 'dev', + isSegwit: true, + isUnsplit: false, + + error: null, + importableAccounts: [], +} + class AccountImporter extends PureComponent { state = INITIAL_STATE @@ -67,142 +84,199 @@ class AccountImporter extends PureComponent { onChangeXPUB = xpub => this.setState({ xpub }) onChangeSegwit = isSegwit => this.setState({ isSegwit }) onChangeUnsplit = isUnsplit => this.setState({ isUnsplit }) + onChangeName = name => this.setState({ name }) isValid = () => { - const { currency, xpub } = this.state - return !!currency && !!xpub + const { currency, xpub, status } = this.state + return !!currency && !!xpub && status !== 'scanning' } scan = async () => { - if (!this.isValid()) return this.setState({ status: 'scanning' }) + const { importableAccounts } = this.state try { - const { currency, xpub, isSegwit, isUnsplit } = this.state - invariant(currency, 'no currency') - const derivationMode = isSegwit - ? isUnsplit - ? 'segwit_unsplit' - : 'segwit' - : isUnsplit - ? 'unsplit' - : '' - const rawAccount = await scanFromXPUB - .send({ - seedIdentifier: 'dev_tool', - currencyId: currency.id, - xpub, - derivationMode, + for (let i = 0; i < importableAccounts.length; i++) { + const a = importableAccounts[i] + const scanPayload = { + seedIdentifier: `dev_${a.xpub}`, + currencyId: a.currency.id, + xpub: a.xpub, + derivationMode: a.derivationMode, + } + const rawAccount = await scanFromXPUB.send(scanPayload).toPromise() + const account = decodeAccount(rawAccount) + await this.import({ + ...account, + name: a.name, }) - .toPromise() - const account = decodeAccount(rawAccount) - this.setState({ status: 'finish', account }) + this.removeImportableAccount(a) + } + this.reset() } catch (error) { this.setState({ status: 'error', error }) } } - import = async () => { - const { account } = this.state + addToScan = () => { + const { xpub, currency, isSegwit, isUnsplit, name } = this.state + const derivationMode = isSegwit + ? isUnsplit + ? 'segwit_unsplit' + : 'segwit' + : isUnsplit + ? 'unsplit' + : '' + const importableAccount = { xpub, currency, derivationMode, name } + this.setState(({ importableAccounts }) => ({ + importableAccounts: [...importableAccounts, importableAccount], + currency: null, + xpub: '', + name: 'dev', + isSegwit: true, + isUnsplit: false, + })) + } + + removeImportableAccount = importableAccount => { + this.setState(({ importableAccounts }) => ({ + importableAccounts: importableAccounts.filter(i => i.xpub !== importableAccount.xpub), + })) + } + + import = async account => { invariant(account, 'no account') await idleCallback() this.props.addAccount(account) - this.reset() } reset = () => this.setState(INITIAL_STATE) render() { - const { currency, xpub, isSegwit, isUnsplit, status, account, error } = this.state + const { + currency, + xpub, + name, + isSegwit, + isUnsplit, + status, + error, + importableAccounts, + } = this.state const supportsSplit = !!currency && !!currency.forkedFrom return ( - - {status === 'idle' ? ( - - - - - - {currency && (currency.supportsSegwit || supportsSplit) ? ( - - {supportsSplit && ( - - - {'unsplit'} + + + {status === 'idle' || status === 'scanning' ? ( + + + + + + {currency && (currency.supportsSegwit || supportsSplit) ? ( + + {supportsSplit && ( + + + {'unsplit'} + + - - - )} - {currency.supportsSegwit && ( - - - {'segwit'} + )} + {currency.supportsSegwit && ( + + + {'segwit'} + + - - - )} - - ) : null} - - - - - - - - - ) : status === 'scanning' ? ( - - - - ) : status === 'finish' ? ( - account ? ( - - - {currency && } - - {account.name} - - {`${account.operations.length} operation(s)`} + )} + ) : null} + + + - - - - ) : ( + + + + + + + + + ) : status === 'error' ? ( - {'No accounts found or wrong xpub'} + + + - ) - ) : status === 'error' ? ( - - - - - - - ) : null} - + ) : null} + + {!!importableAccounts.length && ( + + {importableAccounts.map((acc, i) => ( + + {acc.xpub} + + ))} + {status !== 'scanning' && ( + + + + )} + + )} + + ) + } +} + +class ImportableAccount extends PureComponent<{ + importableAccount: ImportableAccountType, + onRemove: ImportableAccountType => void, + isLoading: boolean, +}> { + remove = () => { + this.props.onRemove(this.props.importableAccount) + } + render() { + const { importableAccount, isLoading } = this.props + return ( + + {isLoading && } + + + {`[${importableAccount.name}] ${importableAccount.derivationMode || + 'default'} ${importableAccount.xpub}`} + + {!isLoading && ( + + {'Remove'} + + )} + ) } } diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index 93467443..c720c27b 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -557,14 +557,12 @@ export async function scanAccountsFromXPUB({ const currency = getCryptoCurrencyById(currencyId) const walletName = getWalletName({ currency, - seedIdentifier: 'debug', + seedIdentifier, derivationMode, }) const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode }) - await wallet.eraseDataSince(new Date(0)) - const index = 0 const isSegwit = isSegwitDerivationMode(derivationMode)