From 3581fac6841011443bd2a16240d0de7d85d94b81 Mon Sep 17 00:00:00 2001 From: meriadec Date: Tue, 4 Sep 2018 18:11:03 +0200 Subject: [PATCH 01/16] Prevent calling setState if unmounted on CurrentAddress --- src/components/CurrentAddress/index.js | 39 ++++++++++++++------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/components/CurrentAddress/index.js b/src/components/CurrentAddress/index.js index 488bf105..bf979fbc 100644 --- a/src/components/CurrentAddress/index.js +++ b/src/components/CurrentAddress/index.js @@ -143,7 +143,26 @@ class CurrentAddress extends PureComponent { copyFeedback: false, } - _isUnmounted = false + componentWillUnmount() { + if (this._timeout) clearTimeout(this._timeout) + } + + renderCopy = copy => { + const { t } = this.props + return ( + } + label={t('app:common.copyAddress')} + onClick={() => { + this.setState({ copyFeedback: true }) + this._timeout = setTimeout(() => this.setState({ copyFeedback: false }), 1e3) + copy() + }} + /> + ) + } + + _timeout: ?TimeoutID = null render() { const { @@ -214,23 +233,7 @@ class CurrentAddress extends PureComponent { onClick={onVerify} /> ) : null} - ( - } - label={t('app:common.copyAddress')} - onClick={() => { - this.setState({ copyFeedback: true }) - setTimeout(() => { - if (this._isUnmounted) return - this.setState({ copyFeedback: false }) - }, 1e3) - copy() - }} - /> - )} - /> + ) From 3c22dbb6a41b8ca6ed4961d25b943a239b485b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Thu, 6 Sep 2018 12:55:12 +0200 Subject: [PATCH 02/16] add migrationNonce mecanism in the walletName generation --- src/helpers/libcore.js | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index a73e3056..754c7ec5 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -29,6 +29,15 @@ const SPLITTED_CURRENCIES = { }, } +// each time there is a breaking change that needs use to clear cache on both libcore and js side, +// we should bump a nonce in this map +// tech notes: +// - this is used to generate walletName for libcore to completely re-sync to a new wallet. +// - by changing walletName we also make the JS db refresh because we handle the accountId changes (walletName is in accountId). +const migrationNonceByCurrency = { + digibyte: 1, +} + export function isValidAddress(core: *, currency: *, address: string): boolean { const addr = new core.NJSAddress(address, currency) return addr.isValid(address, currency) @@ -319,14 +328,16 @@ const createWalletConfig = (core, configMap = {}) => { async function getOrCreateWallet( core: *, - WALLET_IDENTIFIER: string, + walletName: string, currencyId: string, isSegwit: boolean, isUnsplit: boolean, ): NJSWallet { + const migrationNonce = migrationNonceByCurrency[currencyId] + const walletId = walletName + (migrationNonce ? `_${migrationNonce}` : '') const pool = core.getPoolInstance() try { - const wallet = await timeoutTagged('getWallet', 5000, pool.getWallet(WALLET_IDENTIFIER)) + const wallet = await timeoutTagged('getWallet', 5000, pool.getWallet(walletId)) return wallet } catch (err) { const currency = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currencyId)) @@ -346,7 +357,7 @@ async function getOrCreateWallet( const wallet = await timeoutTagged( 'createWallet', 10000, - core.getPoolInstance().createWallet(WALLET_IDENTIFIER, currency, njsWalletConfig), + core.getPoolInstance().createWallet(walletId, currency, njsWalletConfig), ) return wallet } @@ -513,23 +524,13 @@ export async function syncAccount({ const decodedAccountId = accountIdHelper.decode(accountId) const isSegwit = isSegwitPath(freshAddressPath) const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId]) - let njsWallet - try { - njsWallet = await timeoutTagged( - 'getWallet', - 10000, - core.getPoolInstance().getWallet(decodedAccountId.walletName), - ) - } catch (e) { - logger.warn(`Have to reimport the account... (${e})`) - njsWallet = await getOrCreateWallet( - core, - decodedAccountId.walletName, - currencyId, - isSegwit, - isUnsplit, - ) - } + const njsWallet = await getOrCreateWallet( + core, + decodedAccountId.walletName, + currencyId, + isSegwit, + isUnsplit, + ) let njsAccount try { From abf29dda637c1915ff88b4ec82eb4aebd6883958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Thu, 6 Sep 2018 14:43:08 +0200 Subject: [PATCH 03/16] use walletName which is diff from wallet.getName() --- src/helpers/libcore.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index 754c7ec5..e044ff71 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -164,6 +164,7 @@ async function scanAccountsOnDeviceBySegwit({ const accounts = await scanNextAccount({ core, wallet, + walletName, devicePath, currencyId, accountsCount, @@ -241,6 +242,7 @@ const coreSyncAccount = (core, account) => async function scanNextAccount(props: { // $FlowFixMe wallet: NJSWallet, + walletName: string, core: *, devicePath: string, currencyId: string, @@ -256,6 +258,7 @@ async function scanNextAccount(props: { const { core, wallet, + walletName, devicePath, currencyId, accountsCount, @@ -294,6 +297,7 @@ async function scanNextAccount(props: { isUnsplit, accountIndex, wallet, + walletName, currencyId, core, ops, @@ -368,6 +372,7 @@ async function buildAccountRaw({ isSegwit, isUnsplit, wallet, + walletName, currencyId, core, accountIndex, @@ -377,6 +382,7 @@ async function buildAccountRaw({ isSegwit: boolean, isUnsplit: boolean, wallet: NJSWallet, + walletName: string, currencyId: string, accountIndex: number, core: *, @@ -441,7 +447,7 @@ async function buildAccountRaw({ type: 'libcore', version: '1', xpub, - walletName: wallet.getName(), + walletName, }), xpub, path: walletPath, @@ -522,15 +528,10 @@ export async function syncAccount({ index: number, }) { const decodedAccountId = accountIdHelper.decode(accountId) + const { walletName } = decodedAccountId const isSegwit = isSegwitPath(freshAddressPath) const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId]) - const njsWallet = await getOrCreateWallet( - core, - decodedAccountId.walletName, - currencyId, - isSegwit, - isUnsplit, - ) + const njsWallet = await getOrCreateWallet(core, walletName, currencyId, isSegwit, isUnsplit) let njsAccount try { @@ -563,6 +564,7 @@ export async function syncAccount({ isUnsplit, accountIndex: index, wallet: njsWallet, + walletName, currencyId, core, ops, From b6e711346392d3382f5c49c4224d29c25a285026 Mon Sep 17 00:00:00 2001 From: dasilvarosa Date: Fri, 7 Sep 2018 13:47:21 +0200 Subject: [PATCH 04/16] Polish error messages and transaction verification instructions --- static/i18n/en/app.json | 4 ++-- static/i18n/en/errors.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index 4861f0a8..873ea635 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -133,7 +133,7 @@ "title": "Current address", "for": "Address for account <1><0>{{accountName}}", "messageIfUnverified": "Verify the address on your device for optimal security. Press the right button to confirm.", - "messageIfAccepted": "{{currencyName}} address confirmed on your device. Carefully verify when you copy and paste it.", + "messageIfAccepted": "{{currencyName}} address confirmed. Please verify the address if you copy/paste it or if you scan the QR code.", "messageIfSkipped": "Your receive address has not been confirmed on your Ledger device. Please verify your {{currencyName}} address for optimal security." }, "deviceConnect": { @@ -315,7 +315,7 @@ }, "verification": { "title": "Verification", - "warning": "Carefully verify all transaction details now displayed on your device screen\n", + "warning": "Please verify all transaction details now displayed on your device\n", "body": "Once verified, press the right button to confirm and sign the transaction" }, "confirmation": { diff --git a/static/i18n/en/errors.json b/static/i18n/en/errors.json index c4163585..2034612e 100644 --- a/static/i18n/en/errors.json +++ b/static/i18n/en/errors.json @@ -5,7 +5,7 @@ }, "AccountNameRequired": { "title": "An account name is required", - "description": "Please provide with an account name" + "description": "Please provide an account name" }, "BtcUnmatchedApp": { "title": "That's the wrong app", @@ -80,8 +80,8 @@ "description": "Your device was locked. Please unlock it." }, "ManagerNotEnoughSpace": { - "title": "Sorry, insufficient device storage", - "description": "Uninstall some apps to increase available storage and try again." + "title": "Sorry, not enough storage left", + "description": "Please uninstall some apps to make space. This will not affect your crypto assets." }, "ManagerUninstallBTCDep": { "title": "Sorry, this app is required", From 870e8e8a31a0be9308b00bfc6521730f34c25ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Fri, 7 Sep 2018 13:49:44 +0200 Subject: [PATCH 05/16] Also log the POST data --- src/api/network.js | 4 +++- src/logger/logger.js | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/api/network.js b/src/api/network.js index eac84f7f..0da18166 100644 --- a/src/api/network.js +++ b/src/api/network.js @@ -6,7 +6,7 @@ import logger from 'logger' import { LedgerAPIErrorWithMessage, LedgerAPIError, NetworkDown } from 'config/errors' import anonymizer from 'helpers/anonymizer' -const userFriendlyError = (p: Promise, { url, method, startTime }): Promise => +const userFriendlyError = (p: Promise, { url, method, startTime, ...rest }): Promise => p.catch(error => { let errorToThrow if (error.response) { @@ -47,6 +47,7 @@ const userFriendlyError = (p: Promise, { url, method, startTime }): Promis }) } logger.networkError({ + ...rest, status, url, method, @@ -80,6 +81,7 @@ let implementation = (arg: Object) => { const meta = { url: arg.url, method: arg.method, + data: arg.data, startTime: Date.now(), } logger.network(meta) diff --git a/src/logger/logger.js b/src/logger/logger.js index 58eaf83b..072bf7d8 100644 --- a/src/logger/logger.js +++ b/src/logger/logger.js @@ -273,6 +273,7 @@ export default { status, error, responseTime, + ...rest }: { method: string, url: string, @@ -285,7 +286,7 @@ export default { 0, )}ms` if (logNetwork) { - logger.log('info', log, { type: 'network-error', status, method }) + logger.log('info', log, { type: 'network-error', status, method, ...rest }) } captureBreadcrumb({ category: 'network', From 7227be3e6ec88f0b862f2ffc0fb1d55627bd7bd9 Mon Sep 17 00:00:00 2001 From: meriadec Date: Fri, 7 Sep 2018 13:52:55 +0200 Subject: [PATCH 06/16] Drastically increment sync timeouts to address long sync issues --- src/config/constants.js | 2 +- src/helpers/libcore.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index cbe5b29e..06ad1b7d 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -45,7 +45,7 @@ export const SYNC_ALL_INTERVAL = 120 * 1000 export const SYNC_BOOT_DELAY = 2 * 1000 export const SYNC_PENDING_INTERVAL = 10 * 1000 export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 1) -export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 60 * 1000) +export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 5 * 60 * 1000) // Endpoints... diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index e044ff71..240eba5f 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -555,7 +555,7 @@ export async function syncAccount({ unsub() const query = njsAccount.queryOperations() - const ops = await timeoutTagged('ops', 30000, query.complete().execute()) + const ops = await timeoutTagged('ops', 5 * 60 * 1000, query.complete().execute()) const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance()) const syncedRawAccount = await buildAccountRaw({ From 5deaa304136510296f78743e189701de03df019a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 10 Sep 2018 15:31:45 +0200 Subject: [PATCH 07/16] Update DGB support --- package.json | 2 +- src/components/EnsureDeviceApp.js | 6 +++--- src/config/errors.js | 1 + src/helpers/errors.js | 2 +- src/helpers/getAddressForCurrency/btc.js | 13 ++++++++++++- static/i18n/en/errors.json | 4 ++++ yarn.lock | 6 +++--- 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 52dc402b..f82c7b48 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "4.22.0", "@ledgerhq/ledger-core": "2.0.0-rc.6", - "@ledgerhq/live-common": "^3.4.0", + "@ledgerhq/live-common": "^3.5.0", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index 6bb0e89c..c453af7d 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -20,7 +20,7 @@ import IconUsb from 'icons/Usb' import type { Device } from 'types/common' -import { WrongDeviceForAccount, CantOpenDevice, BtcUnmatchedApp } from 'config/errors' +import { WrongDeviceForAccount, CantOpenDevice, UpdateYourApp } from 'config/errors' import { getCurrentDevice } from 'reducers/devices' const usbIcon = @@ -61,10 +61,10 @@ class EnsureDeviceApp extends Component<{ }, { shouldThrow: (err: Error) => { - const isWrongApp = err instanceof BtcUnmatchedApp const isWrongDevice = err instanceof WrongDeviceForAccount const isCantOpenDevice = err instanceof CantOpenDevice - return isWrongApp || isWrongDevice || isCantOpenDevice + const isUpdateYourApp = err instanceof UpdateYourApp + return isWrongDevice || isCantOpenDevice || isUpdateYourApp }, }, ) diff --git a/src/config/errors.js b/src/config/errors.js index 227e0530..f6e4a81c 100644 --- a/src/config/errors.js +++ b/src/config/errors.js @@ -33,6 +33,7 @@ export const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance') export const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch') export const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect') export const TimeoutTagged = createCustomErrorClass('TimeoutTagged') +export const UpdateYourApp = createCustomErrorClass('UpdateYourApp') export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress') export const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate') export const UserRefusedOnDevice = createCustomErrorClass('UserRefusedOnDevice') // TODO rename because it's just for transaction refusal diff --git a/src/helpers/errors.js b/src/helpers/errors.js index 5b6221ab..22436e4b 100644 --- a/src/helpers/errors.js +++ b/src/helpers/errors.js @@ -6,10 +6,10 @@ const errorClasses = {} export const createCustomErrorClass = (name: string): Class => { const C = function CustomError(message?: string, fields?: Object) { + Object.assign(this, fields) this.name = name this.message = message || name this.stack = new Error().stack - Object.assign(this, fields) } // $FlowFixMe C.prototype = new Error() diff --git a/src/helpers/getAddressForCurrency/btc.js b/src/helpers/getAddressForCurrency/btc.js index 8155a25d..69524069 100644 --- a/src/helpers/getAddressForCurrency/btc.js +++ b/src/helpers/getAddressForCurrency/btc.js @@ -3,9 +3,13 @@ import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' import Btc from '@ledgerhq/hw-app-btc' import type Transport from '@ledgerhq/hw-transport' -import { BtcUnmatchedApp } from 'config/errors' +import { BtcUnmatchedApp, UpdateYourApp } from 'config/errors' import getBitcoinLikeInfo from '../devices/getBitcoinLikeInfo' +const oldP2SH = { + digibyte: 5, +} + export default async ( transport: Transport<*>, currency: CryptoCurrency, @@ -25,6 +29,13 @@ export default async ( if (bitcoinLikeInfo) { const { P2SH, P2PKH } = await getBitcoinLikeInfo(transport) if (P2SH !== bitcoinLikeInfo.P2SH || P2PKH !== bitcoinLikeInfo.P2PKH) { + if ( + currency.id in oldP2SH && + P2SH === oldP2SH[currency.id] && + P2PKH === bitcoinLikeInfo.P2PKH + ) { + throw new UpdateYourApp(`UpdateYourApp ${currency.id}`, currency) + } throw new BtcUnmatchedApp(`BtcUnmatchedApp ${currency.id}`, currency) } } diff --git a/static/i18n/en/errors.json b/static/i18n/en/errors.json index 2034612e..043c7038 100644 --- a/static/i18n/en/errors.json +++ b/static/i18n/en/errors.json @@ -139,6 +139,10 @@ "title": "Receive address rejected", "description": "Please try again or contact Ledger Support" }, + "UpdateYourApp": { + "title": "App update required. Uninstall and reinstall the {{managerAppName}} app in the Manager", + "description": null + }, "WebsocketConnectionError": { "title": "Sorry, try again (websocket error).", "description": null diff --git a/yarn.lock b/yarn.lock index 295b54d4..0e14d6cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1549,9 +1549,9 @@ npm "^5.7.1" prebuild-install "^2.2.2" -"@ledgerhq/live-common@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.4.0.tgz#49a9f8865d2e3ea898cfba15d69bdaf32c949f80" +"@ledgerhq/live-common@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.5.0.tgz#3176f5f3cdbe600775edd2b5dac5c10addd90e6f" dependencies: axios "^0.18.0" bignumber.js "^7.2.1" From 65bb796d453719c049f2b28c2a3e84a82379c093 Mon Sep 17 00:00:00 2001 From: meriadec Date: Sun, 9 Sep 2018 17:20:54 +0200 Subject: [PATCH 08/16] Add DevTools page and ability to import accounts via xpub --- src/commands/index.js | 2 + src/commands/libcoreScanFromXPUB.js | 29 +++ .../DevToolsPage/AccountImporter.js | 207 ++++++++++++++++++ src/components/DevToolsPage/index.js | 11 + src/components/MainSideBar/index.js | 32 ++- src/components/base/Input/index.js | 2 +- src/components/layout/Default.js | 2 + src/config/cryptocurrencies.js | 9 + src/helpers/libcore.js | 77 +++++-- static/i18n/en/app.json | 3 +- 10 files changed, 356 insertions(+), 18 deletions(-) create mode 100644 src/commands/libcoreScanFromXPUB.js create mode 100644 src/components/DevToolsPage/AccountImporter.js create mode 100644 src/components/DevToolsPage/index.js diff --git a/src/commands/index.js b/src/commands/index.js index 8b1ff369..117a8b3f 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -18,6 +18,7 @@ import isDashboardOpen from 'commands/isDashboardOpen' import libcoreGetFees from 'commands/libcoreGetFees' import libcoreGetVersion from 'commands/libcoreGetVersion' import libcoreScanAccounts from 'commands/libcoreScanAccounts' +import libcoreScanFromXPUB from 'commands/libcoreScanFromXPUB' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreSyncAccount from 'commands/libcoreSyncAccount' import libcoreValidAddress from 'commands/libcoreValidAddress' @@ -49,6 +50,7 @@ const all: Array> = [ libcoreGetFees, libcoreGetVersion, libcoreScanAccounts, + libcoreScanFromXPUB, libcoreSignAndBroadcast, libcoreSyncAccount, libcoreValidAddress, diff --git a/src/commands/libcoreScanFromXPUB.js b/src/commands/libcoreScanFromXPUB.js new file mode 100644 index 00000000..1c595861 --- /dev/null +++ b/src/commands/libcoreScanFromXPUB.js @@ -0,0 +1,29 @@ +// @flow + +import { fromPromise } from 'rxjs/observable/fromPromise' +import type { AccountRaw } from '@ledgerhq/live-common/lib/types' + +import { createCommand, Command } from 'helpers/ipc' +import withLibcore from 'helpers/withLibcore' +import { scanAccountsFromXPUB } from 'helpers/libcore' + +type Input = { + currencyId: string, + xpub: string, + isSegwit: boolean, + isUnsplit: boolean, +} + +type Result = AccountRaw + +const cmd: Command = createCommand( + 'libcoreScanFromXPUB', + ({ currencyId, xpub, isSegwit, isUnsplit }) => + fromPromise( + withLibcore(async core => + scanAccountsFromXPUB({ core, currencyId, xpub, isSegwit, isUnsplit }), + ), + ), +) + +export default cmd diff --git a/src/components/DevToolsPage/AccountImporter.js b/src/components/DevToolsPage/AccountImporter.js new file mode 100644 index 00000000..88afd7c9 --- /dev/null +++ b/src/components/DevToolsPage/AccountImporter.js @@ -0,0 +1,207 @@ +// @flow + +import React, { PureComponent, Fragment } from 'react' +import invariant from 'invariant' +import { connect } from 'react-redux' + +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 Switch from 'components/base/Switch' +import Spinner from 'components/base/Spinner' +import Box, { Card } from 'components/base/Box' +import TranslatedError from 'components/TranslatedError' +import Button from 'components/base/Button' +import Input from 'components/base/Input' +import Label from 'components/base/Label' +import SelectCurrency from 'components/SelectCurrency' +import { CurrencyCircleIcon } from 'components/base/CurrencyBadge' + +import { idleCallback } from 'helpers/promise' +import { splittedCurrencies } from 'config/cryptocurrencies' + +import scanFromXPUB from 'commands/libcoreScanFromXPUB' + +const mapDispatchToProps = { + addAccount, +} + +type Props = { + addAccount: Account => void, +} + +const INITIAL_STATE = { + status: 'idle', + currency: null, + xpub: '', + account: null, + isSegwit: true, + isUnsplit: false, + error: null, +} + +type State = { + status: string, + currency: ?Currency, + xpub: string, + account: ?Account, + isSegwit: boolean, + isUnsplit: boolean, + error: ?Error, +} + +class AccountImporter extends PureComponent { + state = INITIAL_STATE + + onChangeCurrency = currency => { + if (currency.family !== 'bitcoin') return + this.setState({ + currency, + isSegwit: !!currency.supportsSegwit, + isUnsplit: false, + }) + } + + onChangeXPUB = xpub => this.setState({ xpub }) + onChangeSegwit = isSegwit => this.setState({ isSegwit }) + onChangeUnsplit = isUnsplit => this.setState({ isUnsplit }) + + isValid = () => { + const { currency, xpub } = this.state + return !!currency && !!xpub + } + + scan = async () => { + if (!this.isValid()) return + this.setState({ status: 'scanning' }) + try { + const { currency, xpub, isSegwit, isUnsplit } = this.state + invariant(currency, 'no currency') + const rawAccount = await scanFromXPUB + .send({ + currencyId: currency.id, + xpub, + isSegwit, + isUnsplit, + }) + .toPromise() + const account = decodeAccount(rawAccount) + this.setState({ status: 'finish', account }) + } catch (error) { + this.setState({ status: 'error', error }) + } + } + + import = async () => { + const { account } = this.state + 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 supportsSplit = !!currency && !!splittedCurrencies[currency.id] + return ( + + {status === 'idle' ? ( + + + + + + {currency && (currency.supportsSegwit || supportsSplit) ? ( + + {supportsSplit && ( + + + {'unsplit'} + + + + )} + {currency.supportsSegwit && ( + + + {'segwit'} + + + + )} + + ) : null} + + + + + + + + + ) : status === 'scanning' ? ( + + + + ) : status === 'finish' ? ( + account ? ( + + + {currency && } + + {account.name} + + {`${account.operations.length} operation(s)`} + + + + + + ) : ( + + {'No accounts found or wrong xpub'} + + + ) + ) : status === 'error' ? ( + + + + + + + ) : null} + + ) + } +} + +export default connect( + null, + mapDispatchToProps, +)(AccountImporter) diff --git a/src/components/DevToolsPage/index.js b/src/components/DevToolsPage/index.js new file mode 100644 index 00000000..bc849837 --- /dev/null +++ b/src/components/DevToolsPage/index.js @@ -0,0 +1,11 @@ +import React from 'react' + +import Box from 'components/base/Box' + +import AccountImporter from './AccountImporter' + +export default () => ( + + + +) diff --git a/src/components/MainSideBar/index.js b/src/components/MainSideBar/index.js index 1c67e87f..e1cb9051 100644 --- a/src/components/MainSideBar/index.js +++ b/src/components/MainSideBar/index.js @@ -19,6 +19,7 @@ import { i } from 'helpers/staticPath' import { accountsSelector } from 'reducers/accounts' import { openModal } from 'reducers/modals' import { getUpdateStatus } from 'reducers/update' +import { developerModeSelector } from 'reducers/settings' import { SideBarList, SideBarListItem } from 'components/base/SideBar' import Box from 'components/base/Box' @@ -38,6 +39,7 @@ import TopGradient from './TopGradient' const mapStateToProps = state => ({ accounts: accountsSelector(state), updateStatus: getUpdateStatus(state), + developerMode: developerModeSelector(state), }) const mapDispatchToProps = { @@ -52,8 +54,26 @@ type Props = { push: string => void, openModal: string => void, updateStatus: UpdateStatus, + developerMode: boolean, } +const IconDev = () => ( +
+ {'DEV'} +
+) + class MainSideBar extends PureComponent { push = (to: string) => { const { push } = this.props @@ -78,10 +98,11 @@ class MainSideBar extends PureComponent { handleOpenReceiveModal = () => this.props.openModal(MODAL_RECEIVE) handleClickManager = () => this.push('/manager') handleClickExchange = () => this.push('/exchange') + handleClickDev = () => this.push('/dev') handleOpenImportModal = () => this.props.openModal(MODAL_ADD_ACCOUNTS) render() { - const { t, accounts, location, updateStatus } = this.props + const { t, accounts, location, updateStatus, developerMode } = this.props const { pathname } = location const addAccountButton = ( @@ -133,6 +154,15 @@ class MainSideBar extends PureComponent { onClick={this.handleClickExchange} isActive={pathname === '/exchange'} /> + {developerMode && ( + + )} ) => void, onChange?: Function, - onEnter?: (SyntheticKeyboardEvent) => void, + onEnter?: (SyntheticKeyboardEvent) => *, onEsc?: (SyntheticKeyboardEvent) => void, onFocus: (SyntheticInputEvent) => void, renderLeft?: any, diff --git a/src/components/layout/Default.js b/src/components/layout/Default.js index 5d70f1db..ca47557f 100644 --- a/src/components/layout/Default.js +++ b/src/components/layout/Default.js @@ -19,6 +19,7 @@ import AccountPage from 'components/AccountPage' import DashboardPage from 'components/DashboardPage' import ManagerPage from 'components/ManagerPage' import ExchangePage from 'components/ExchangePage' +import DevToolsPage from 'components/DevToolsPage' import SettingsPage from 'components/SettingsPage' import KeyboardContent from 'components/KeyboardContent' import PerfIndicator from 'components/PerfIndicator' @@ -110,6 +111,7 @@ class Default extends Component { + diff --git a/src/config/cryptocurrencies.js b/src/config/cryptocurrencies.js index 2667ddb7..21f56322 100644 --- a/src/config/cryptocurrencies.js +++ b/src/config/cryptocurrencies.js @@ -35,3 +35,12 @@ export const listCryptoCurrencies = memoize((withDevCrypto?: boolean) => .filter(c => supported.includes(c.id)) .sort((a, b) => a.name.localeCompare(b.name)), ) + +export const splittedCurrencies = { + bitcoin_cash: { + coinType: 0, + }, + bitcoin_gold: { + coinType: 0, + }, +} diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index 240eba5f..5f7f20e5 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -15,20 +15,11 @@ import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgerc import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' import * as accountIdHelper from 'helpers/accountId' import { NoAddressesFound } from 'config/errors' +import { splittedCurrencies } from 'config/cryptocurrencies' import { deserializeError } from './errors' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName' import { timeoutTagged } from './promise' -// TODO: put that info inside currency itself -const SPLITTED_CURRENCIES = { - bitcoin_cash: { - coinType: 0, - }, - bitcoin_gold: { - coinType: 0, - }, -} - // each time there is a breaking change that needs use to clear cache on both libcore and js side, // we should bump a nonce in this map // tech notes: @@ -84,7 +75,7 @@ export async function scanAccountsOnDevice(props: Props): Promise } // TODO: put that info inside currency itself - if (currencyId in SPLITTED_CURRENCIES) { + if (currencyId in splittedCurrencies) { const splittedAccounts = await scanAccountsOnDeviceBySegwit({ ...commonParams, isSegwit: false, @@ -118,7 +109,7 @@ function encodeWalletName({ isSegwit: boolean, isUnsplit: boolean, }) { - const splitConfig = isUnsplit ? SPLITTED_CURRENCIES[currencyId] || null : null + const splitConfig = isUnsplit ? splittedCurrencies[currencyId] || null : null return `${publicKey}__${currencyId}${isSegwit ? '_segwit' : ''}${splitConfig ? '_unsplit' : ''}` } @@ -142,7 +133,7 @@ async function scanAccountsOnDeviceBySegwit({ isUnsplit: boolean, }): Promise { const customOpts = - isUnsplit && SPLITTED_CURRENCIES[currencyId] ? SPLITTED_CURRENCIES[currencyId] : null + isUnsplit && splittedCurrencies[currencyId] ? splittedCurrencies[currencyId] : null const { coinType } = customOpts ? customOpts.coinType : getCryptoCurrencyById(currencyId) const path = `${isSegwit ? '49' : '44'}'/${coinType}'` @@ -345,7 +336,7 @@ async function getOrCreateWallet( return wallet } catch (err) { const currency = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currencyId)) - const splitConfig = isUnsplit ? SPLITTED_CURRENCIES[currencyId] || null : null + const splitConfig = isUnsplit ? splittedCurrencies[currencyId] || null : null const coinType = splitConfig ? splitConfig.coinType : '' const walletConfig = isSegwit ? { @@ -530,7 +521,7 @@ export async function syncAccount({ const decodedAccountId = accountIdHelper.decode(accountId) const { walletName } = decodedAccountId const isSegwit = isSegwitPath(freshAddressPath) - const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId]) + const isUnsplit = isUnsplitPath(freshAddressPath, splittedCurrencies[currencyId]) const njsWallet = await getOrCreateWallet(core, walletName, currencyId, isSegwit, isUnsplit) let njsAccount @@ -584,3 +575,59 @@ export function libcoreAmountToBigNumber(njsAmount: *): BigNumber { export function bigNumberToLibcoreAmount(core: *, njsWalletCurrency: *, bigNumber: BigNumber) { return new core.NJSAmount(njsWalletCurrency, 0).fromHex(njsWalletCurrency, bigNumber.toString(16)) } + +export async function scanAccountsFromXPUB({ + core, + currencyId, + xpub, + isSegwit, + isUnsplit, +}: { + core: *, + currencyId: string, + xpub: string, + isSegwit: boolean, + isUnsplit: boolean, +}) { + const currency = getCryptoCurrencyById(currencyId) + const walletName = encodeWalletName({ + publicKey: `debug_${xpub}`, + currencyId, + isSegwit, + isUnsplit, + }) + + const wallet = await getOrCreateWallet(core, walletName, currencyId, isSegwit, isUnsplit) + + await wallet.eraseDataSince(new Date(0)) + + const index = 0 + + const extendedInfos = { + index, + owners: ['main'], + derivations: [ + `${isSegwit ? '49' : '44'}'/${currency.coinType}'`, + `${isSegwit ? '49' : '44'}'/${currency.coinType}'/0`, + ], + extendedKeys: [xpub], + } + + const account = await wallet.newAccountWithExtendedKeyInfo(extendedInfos) + await coreSyncAccount(core, account) + const query = account.queryOperations() + const ops = await query.complete().execute() + const rawAccount = await buildAccountRaw({ + njsAccount: account, + isSegwit, + isUnsplit, + accountIndex: index, + wallet, + walletName, + currencyId, + core, + ops, + }) + + return rawAccount +} diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index 873ea635..b94db001 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -78,7 +78,8 @@ "menu": "Menu", "accounts": "Accounts ({{count}})", "manager": "Manager", - "exchange": "Buy/Trade" + "exchange": "Buy/Trade", + "developer": "Dev tools" }, "account": { "lastOperations": "Last operations", From 92dd2d95287a7c3ad9f5411b092ca31c881e9bcd Mon Sep 17 00:00:00 2001 From: meriadec Date: Mon, 10 Sep 2018 17:29:50 +0200 Subject: [PATCH 09/16] Bump ledger-core to v2.0.0-rc.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f82c7b48..c4ff09fe 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@ledgerhq/hw-app-xrp": "^4.13.0", "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "4.22.0", - "@ledgerhq/ledger-core": "2.0.0-rc.6", + "@ledgerhq/ledger-core": "2.0.0-rc.7", "@ledgerhq/live-common": "^3.5.0", "animated": "^0.2.2", "async": "^2.6.1", From c164dbee82438caeccb9bdac141d85d1ef7e61f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 10 Sep 2018 17:48:31 +0200 Subject: [PATCH 10/16] use libcore inputs and outputs to return the optimistic update --- src/commands/libcoreSignAndBroadcast.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index e479c9f3..83d36a2b 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -164,7 +164,6 @@ export async function doSignAndBroadcast({ accountId, currencyId, xpub, - freshAddress, freshAddressPath, index, transaction, @@ -237,6 +236,16 @@ export async function doSignAndBroadcast({ .asBitcoinLikeAccount() .broadcastRawTransaction(Array.from(Buffer.from(signedTransaction, 'hex'))) + const senders = builded + .getInputs() + .map(input => input.getAddress()) + .filter(a => a) + + const recipients = builded + .getOutputs() + .map(output => output.getAddress()) + .filter(a => a) + const fee = libcoreAmountToBigNumber(builded.getFees()) // NB we don't check isCancelled() because the broadcast is not cancellable now! @@ -250,9 +259,8 @@ export async function doSignAndBroadcast({ fee: fee.toString(), blockHash: null, blockHeight: null, - // FIXME for senders and recipients, can we ask the libcore? - senders: [freshAddress], - recipients: [transaction.recipient], + senders, + recipients, accountId, date: new Date().toISOString(), }) From 354d5bd5f02db2e96c625899dbb3d27ca3a07ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 10 Sep 2018 17:29:49 +0200 Subject: [PATCH 11/16] Fix checking the tagId validity --- src/components/AdvancedOptions/RippleKind.js | 58 ++++++++++++-------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/components/AdvancedOptions/RippleKind.js b/src/components/AdvancedOptions/RippleKind.js index b87cff5f..74b0b5a4 100644 --- a/src/components/AdvancedOptions/RippleKind.js +++ b/src/components/AdvancedOptions/RippleKind.js @@ -1,5 +1,6 @@ // @flow -import React from 'react' +import React, { Component } from 'react' +import { BigNumber } from 'bignumber.js' import { translate } from 'react-i18next' import Box from 'components/base/Box' @@ -13,24 +14,37 @@ type Props = { t: *, } -export default translate()(({ tag, onChangeTag, t }: Props) => ( - - - - - - - { - const tag = parseInt(str, 10) - if (!isNaN(tag) && isFinite(tag)) onChangeTag(tag) - else onChangeTag(undefined) - }} - /> - - - -)) +const uint32maxPlus1 = BigNumber(2).pow(32) + +class RippleKind extends Component { + onChange = str => { + const { onChangeTag } = this.props + const tag = BigNumber(str.replace(/[^0-9]/g, '')) + if (!tag.isNaN() && tag.isFinite()) { + if (tag.isInteger() && tag.isPositive() && tag.lt(uint32maxPlus1)) { + onChangeTag(tag.toNumber()) + } + } else { + onChangeTag(undefined) + } + } + render() { + const { tag, t } = this.props + return ( + + + + + + + + + + + ) + } +} + +export default translate()(RippleKind) From 573dca8e6664b58383cfa723fe7a275d5ad89af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 10 Sep 2018 18:12:16 +0200 Subject: [PATCH 12/16] update lib --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c4ff09fe..2b3bc8b5 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "4.22.0", "@ledgerhq/ledger-core": "2.0.0-rc.7", - "@ledgerhq/live-common": "^3.5.0", + "@ledgerhq/live-common": "^3.5.1", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", diff --git a/yarn.lock b/yarn.lock index 0e14d6cc..71c73832 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1536,9 +1536,9 @@ dependencies: events "^2.0.0" -"@ledgerhq/ledger-core@2.0.0-rc.6": - version "2.0.0-rc.6" - resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.6.tgz#a08c84bd91c680cd731e1030ce081a9b568a2c47" +"@ledgerhq/ledger-core@2.0.0-rc.7": + version "2.0.0-rc.7" + resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.7.tgz#36a2573f01a1e19c51c6e39692e6b0f8be6c3a77" dependencies: "@ledgerhq/hw-app-btc" "^4.7.3" "@ledgerhq/hw-transport-node-hid" "^4.7.6" @@ -1549,9 +1549,9 @@ npm "^5.7.1" prebuild-install "^2.2.2" -"@ledgerhq/live-common@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.5.0.tgz#3176f5f3cdbe600775edd2b5dac5c10addd90e6f" +"@ledgerhq/live-common@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.5.1.tgz#dab3eb061f361999a9e04ef564808831faac61ea" dependencies: axios "^0.18.0" bignumber.js "^7.2.1" From 187e21f099c2358eaa638d4a5fb31b57b348b20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 10 Sep 2018 19:03:29 +0200 Subject: [PATCH 13/16] Fix using getOrCreateWallet for all access to getWallet --- src/bridge/LibcoreBridge.js | 5 ++--- src/commands/libcoreGetFees.js | 28 ++++++++++++++++++++++--- src/commands/libcoreSignAndBroadcast.js | 14 ++++++++++--- src/helpers/libcore.js | 2 +- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index 642788d1..c9b9535d 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -9,7 +9,7 @@ import FeesBitcoinKind from 'components/FeesField/BitcoinKind' import libcoreScanAccounts from 'commands/libcoreScanAccounts' import libcoreSyncAccount from 'commands/libcoreSyncAccount' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' -import libcoreGetFees from 'commands/libcoreGetFees' +import libcoreGetFees, { extractGetFeesInputFromAccount } from 'commands/libcoreGetFees' import libcoreValidAddress from 'commands/libcoreValidAddress' import { NotEnoughBalance } from 'config/errors' import type { WalletBridge, EditProps } from './types' @@ -85,8 +85,7 @@ const getFees = async (a, transaction) => { if (promise) return promise promise = libcoreGetFees .send({ - accountId: a.id, - accountIndex: a.index, + ...extractGetFeesInputFromAccount(a), transaction: serializeTransaction(transaction), }) .toPromise() diff --git a/src/commands/libcoreGetFees.js b/src/commands/libcoreGetFees.js index ab96ff30..58e8512c 100644 --- a/src/commands/libcoreGetFees.js +++ b/src/commands/libcoreGetFees.js @@ -4,9 +4,17 @@ import { Observable } from 'rxjs' import { BigNumber } from 'bignumber.js' import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' +import type { Account } from '@ledgerhq/live-common/lib/types' import * as accountIdHelper from 'helpers/accountId' -import { isValidAddress, libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore' +import { + isValidAddress, + libcoreAmountToBigNumber, + bigNumberToLibcoreAmount, + getOrCreateWallet, +} from 'helpers/libcore' +import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' import { InvalidAddress } from 'config/errors' +import { splittedCurrencies } from 'config/cryptocurrencies' type BitcoinLikeTransaction = { // TODO we rename this Transaction concept into transactionInput @@ -19,20 +27,34 @@ type Input = { accountId: string, accountIndex: number, transaction: BitcoinLikeTransaction, + currencyId: string, + isSegwit: boolean, + isUnsplit: boolean, +} + +export const extractGetFeesInputFromAccount = (a: Account) => { + const currencyId = a.currency.id + return { + accountId: a.id, + accountIndex: a.index, + currencyId, + isSegwit: isSegwitPath(a.freshAddressPath), + isUnsplit: isUnsplitPath(a.freshAddressPath, splittedCurrencies[currencyId]), + } } type Result = { totalFees: string } const cmd: Command = createCommand( 'libcoreGetFees', - ({ accountId, accountIndex, transaction }) => + ({ accountId, currencyId, isSegwit, isUnsplit, accountIndex, transaction }) => Observable.create(o => { let unsubscribed = false const isCancelled = () => unsubscribed withLibcore(async core => { const { walletName } = accountIdHelper.decode(accountId) - const njsWallet = await core.getPoolInstance().getWallet(walletName) + const njsWallet = await getOrCreateWallet(core, walletName, currencyId, isSegwit, isUnsplit) if (isCancelled()) return const njsAccount = await njsWallet.getAccount(accountIndex) if (isCancelled()) return diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index 83d36a2b..d1fbd814 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -6,8 +6,13 @@ import type { OperationRaw } from '@ledgerhq/live-common/lib/types' import Btc from '@ledgerhq/hw-app-btc' import { Observable } from 'rxjs' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' -import { isSegwitPath } from 'helpers/bip32' -import { libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore' +import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' +import { + libcoreAmountToBigNumber, + bigNumberToLibcoreAmount, + getOrCreateWallet, +} from 'helpers/libcore' +import { splittedCurrencies } from 'config/cryptocurrencies' import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' @@ -187,7 +192,10 @@ export async function doSignAndBroadcast({ onOperationBroadcasted: (optimisticOp: $Exact) => void, }): Promise { const { walletName } = accountIdHelper.decode(accountId) - const njsWallet = await core.getPoolInstance().getWallet(walletName) + + const isSegwit = isSegwitPath(freshAddressPath) + const isUnsplit = isUnsplitPath(freshAddressPath, splittedCurrencies[currencyId]) + const njsWallet = await getOrCreateWallet(core, walletName, currencyId, isSegwit, isUnsplit) if (isCancelled()) return const njsAccount = await njsWallet.getAccount(index) if (isCancelled()) return diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index 5f7f20e5..395b3b64 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -321,7 +321,7 @@ const createWalletConfig = (core, configMap = {}) => { return config } -async function getOrCreateWallet( +export async function getOrCreateWallet( core: *, walletName: string, currencyId: string, From f073039dc045550a91ccc35a9deed365d647567a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 11 Sep 2018 08:08:12 +0200 Subject: [PATCH 14/16] Use SYNC_TIMEOUT in query.complete().execute() too --- src/helpers/libcore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index 5f7f20e5..8be72a98 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -7,7 +7,7 @@ import { BigNumber } from 'bignumber.js' import Btc from '@ledgerhq/hw-app-btc' import { withDevice } from 'helpers/deviceAccess' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' -import { SHOW_LEGACY_NEW_ACCOUNT } from 'config/constants' +import { SHOW_LEGACY_NEW_ACCOUNT, SYNC_TIMEOUT } from 'config/constants' import type { AccountRaw, OperationRaw, OperationType } from '@ledgerhq/live-common/lib/types' import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc' @@ -546,7 +546,7 @@ export async function syncAccount({ unsub() const query = njsAccount.queryOperations() - const ops = await timeoutTagged('ops', 5 * 60 * 1000, query.complete().execute()) + const ops = await timeoutTagged('ops', SYNC_TIMEOUT, query.complete().execute()) const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance()) const syncedRawAccount = await buildAccountRaw({ From b03195c545b32b59be403c04440324361ba8ed49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 11 Sep 2018 13:04:43 +0200 Subject: [PATCH 15/16] Disable Ripple & ZenCash install (now XRP and Horizen) --- src/components/ManagerPage/AppsList.js | 5 ++++- src/components/ManagerPage/ManagerApp.js | 26 +++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/ManagerPage/AppsList.js b/src/components/ManagerPage/AppsList.js index e2f8bf1c..f589ea7f 100644 --- a/src/components/ManagerPage/AppsList.js +++ b/src/components/ManagerPage/AppsList.js @@ -71,6 +71,9 @@ type State = { mode: Mode, } +const oldAppsInstallDisabled = ['ZenCash', 'Ripple'] +const canHandleInstall = c => !oldAppsInstallDisabled.includes(c.name) + const LoadingApp = () => ( @@ -285,7 +288,7 @@ class AppsList extends PureComponent { name={c.name} version={`Version ${c.version}`} icon={ICONS_FALLBACK[c.icon] || c.icon} - onInstall={this.handleInstallApp(c)} + onInstall={canHandleInstall(c) ? this.handleInstallApp(c) : null} onUninstall={this.handleUninstallApp(c)} /> ))} diff --git a/src/components/ManagerPage/ManagerApp.js b/src/components/ManagerPage/ManagerApp.js index 9f4b4494..eb50b073 100644 --- a/src/components/ManagerPage/ManagerApp.js +++ b/src/components/ManagerPage/ManagerApp.js @@ -49,7 +49,7 @@ type Props = { name: string, version: string, icon: string, - onInstall: Function, + onInstall?: Function, onUninstall: Function, } @@ -64,17 +64,19 @@ function ManagerApp({ name, version, icon, onInstall, onUninstall, t }: Props) { {version} - + {onInstall ? ( + + ) : null}