Browse Source

Merge branch 'develop' into qrcodeexporter-new-version

gre-patch-1
Gaëtan Renaudeau 6 years ago
parent
commit
33a25cf195
No known key found for this signature in database GPG Key ID: 7B66B85F042E5451
  1. 2
      .circleci/config.yml
  2. 1
      .gitignore
  3. 12
      package.json
  4. 17
      scripts/download-xliffs.sh
  5. 100
      scripts/release.sh
  6. 23
      src/bridge/EthereumJSBridge.js
  7. 1
      src/bridge/LibcoreBridge.js
  8. 11
      src/bridge/RippleJSBridge.js
  9. 4
      src/commands/libcoreGetFees.js
  10. 4
      src/commands/libcoreScanFromXPUB.js
  11. 49
      src/commands/libcoreSignAndBroadcast.js
  12. 4
      src/commands/libcoreSyncAccount.js
  13. 304
      src/components/DevToolsPage/AccountImporter.js
  14. 4
      src/components/modals/AddAccounts/steps/03-step-import.js
  15. 2
      src/config/urls.js
  16. 2
      src/helpers/countervalues.js
  17. 44
      src/helpers/libcore.js
  18. 1
      src/internals/index.js
  19. 6
      static/i18n/en/app.json
  20. 60
      yarn.lock

2
.circleci/config.yml

@ -9,7 +9,7 @@ jobs:
build: build:
<<: *defaults <<: *defaults
steps: steps:
- run: sudo apt-get install -y libudev-dev - run: sudo apt-get install -y libudev-dev libfuse-dev
- run: - run:
name: Install latest yarn name: Install latest yarn
command: | command: |

1
.gitignore

@ -1,3 +1,4 @@
xliffs/
/.env /.env
/dist/ /dist/
/flow-typed/ /flow-typed/

12
package.json

@ -3,7 +3,7 @@
"productName": "Ledger Live", "productName": "Ledger Live",
"description": "Ledger Live - Desktop", "description": "Ledger Live - Desktop",
"repository": "https://github.com/LedgerHQ/ledger-live-desktop", "repository": "https://github.com/LedgerHQ/ledger-live-desktop",
"version": "1.2.1", "version": "1.2.3",
"author": "Ledger", "author": "Ledger",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -35,13 +35,13 @@
} }
}, },
"dependencies": { "dependencies": {
"@ledgerhq/hw-app-btc": "4.24.0", "@ledgerhq/hw-app-btc": "^4.27.0",
"@ledgerhq/hw-app-eth": "^4.24.0", "@ledgerhq/hw-app-eth": "^4.24.0",
"@ledgerhq/hw-app-xrp": "^4.24.0", "@ledgerhq/hw-app-xrp": "^4.25.0",
"@ledgerhq/hw-transport": "^4.24.0", "@ledgerhq/hw-transport": "^4.24.0",
"@ledgerhq/hw-transport-node-hid": "4.24.0", "@ledgerhq/hw-transport-node-hid": "4.24.0",
"@ledgerhq/ledger-core": "2.0.0-rc.8", "@ledgerhq/ledger-core": "2.0.0-rc.11",
"@ledgerhq/live-common": "4.0.0-beta.1", "@ledgerhq/live-common": "4.3.0",
"animated": "^0.2.2", "animated": "^0.2.2",
"async": "^2.6.1", "async": "^2.6.1",
"axios": "^0.18.0", "axios": "^0.18.0",
@ -74,7 +74,7 @@
"qs": "^6.5.1", "qs": "^6.5.1",
"raven": "^2.5.0", "raven": "^2.5.0",
"raven-js": "^3.24.2", "raven-js": "^3.24.2",
"react": "^16.4.1", "react": "^16.6.1",
"react-dom": "^16.4.1", "react-dom": "^16.4.1",
"react-i18next": "^7.7.0", "react-i18next": "^7.7.0",
"react-key-handler": "^1.0.1", "react-key-handler": "^1.0.1",

17
scripts/download-xliffs.sh

@ -0,0 +1,17 @@
#!/bin/sh
if [ -z "$CROWDIN_TOKEN" ]; then
echo "CROWDIN_TOKEN env required" >&2
exit 1
fi
rm -rf xliffs
mkdir xliffs
cd xliffs
for lang in fr es-ES zh-CN ja ko ru; do
curl "https://api.crowdin.com/api/project/ledger-wallet/export-file?file=develop/static/i18n/en/app.json&language=$lang&format=xliff&key=$CROWDIN_TOKEN" > en-$lang.xliff
done
zip -r ledger-live-langs.zip *.xliff

100
scripts/release.sh

@ -69,48 +69,58 @@ fi
runJob "yarn compile" "compiling..." "compiled" "failed to compile" "verbose" runJob "yarn compile" "compiling..." "compiled" "failed to compile" "verbose"
# -------------------------------------------------------------------- if [[ $(uname) == 'Linux' ]]; then
# Linux: Internal process error (null) # --------------------------------------------------------------------
# # Linux: Internal process error (null)
# context: https://github.com/LedgerHQ/ledger-live-desktop/issues/1010 #
# Linux: Internal process error (null) # context: https://github.com/LedgerHQ/ledger-live-desktop/issues/1010
# # Linux: Internal process error (null)
# The "fix" is not optimal, as it doesn't really solve the problem #
# (electron loading system openssl before we can load our embedded one) # The "fix" is not optimal, as it doesn't really solve the problem
# Quick summary: # (electron loading system openssl before we can load our embedded one)
# # Quick summary:
# - build without publishing #
# - unpack the .AppImage # - build without publishing
# - download reported working libs from ubuntu mirrors, put it inside # - unpack the .AppImage
# - re-pack the .AppImage # - download reported working libs from ubuntu mirrors, put it inside
# - checksum stuff # - re-pack the .AppImage
# - upload to gh # - checksum stuff
# - upload to gh
runJob \
"DEBUG=electron-builder electron-builder build --publish never" \ runJob \
"building and packaging app..." \ "DEBUG=electron-builder electron-builder build --publish never" \
"app built and packaged successfully" \ "building and packaging app..." \
"failed to build app" \ "app built and packaged successfully" \
"verbose" "failed to build app" \
"verbose"
runJob \
"scripts/patch-appimage.sh" \ runJob \
"patching AppImage..." \ "scripts/patch-appimage.sh" \
"AppImage patched successfully" \ "patching AppImage..." \
"failed to patch AppImage" "AppImage patched successfully" \
"failed to patch AppImage"
LEDGER_LIVE_VERSION=$(grep version package.json | sed -E 's/.*: "(.*)",/\1/g')
LEDGER_LIVE_VERSION=$(grep version package.json | sed -E 's/.*: "(.*)",/\1/g')
scripts/upload-github-release-asset.sh \
github_api_token="$GH_TOKEN" \ scripts/upload-github-release-asset.sh \
owner=LedgerHQ \ github_api_token="$GH_TOKEN" \
repo=ledger-live-desktop \ owner=LedgerHQ \
tag="$GH_TAG" \ repo=ledger-live-desktop \
filename="dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage" tag="$GH_TAG" \
filename="dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage"
scripts/upload-github-release-asset.sh \
github_api_token="$GH_TOKEN" \ scripts/upload-github-release-asset.sh \
owner=LedgerHQ \ github_api_token="$GH_TOKEN" \
repo=ledger-live-desktop \ owner=LedgerHQ \
tag="$GH_TAG" \ repo=ledger-live-desktop \
filename="dist/latest-linux.yml" tag="$GH_TAG" \
filename="dist/latest-linux.yml"
else
runJob \
"DEBUG=electron-builder electron-builder build --publish always" \
"building and packaging app..." \
"app built and packaged successfully" \
"failed to build app" \
"verbose"
fi

23
src/bridge/EthereumJSBridge.js

@ -12,6 +12,7 @@ import {
getDerivationModesForCurrency, getDerivationModesForCurrency,
getDerivationScheme, getDerivationScheme,
runDerivationScheme, runDerivationScheme,
isIterableDerivationMode,
getMandatoryEmptyAccountSkip, getMandatoryEmptyAccountSkip,
} from '@ledgerhq/live-common/lib/derivation' } from '@ledgerhq/live-common/lib/derivation'
import { import {
@ -72,7 +73,7 @@ const txToOps = (account: Account) => (tx: Tx): Operation[] => {
const value = BigNumber(tx.value) const value = BigNumber(tx.value)
const fee = BigNumber(tx.gas_price * tx.gas_used) const fee = BigNumber(tx.gas_price * tx.gas_used)
if (sending) { if (sending) {
ops.push({ const op: $Exact<Operation> = {
id: `${account.id}-${tx.hash}-OUT`, id: `${account.id}-${tx.hash}-OUT`,
hash: tx.hash, hash: tx.hash,
type: 'OUT', type: 'OUT',
@ -84,10 +85,12 @@ const txToOps = (account: Account) => (tx: Tx): Operation[] => {
senders: [tx.from], senders: [tx.from],
recipients: [tx.to], recipients: [tx.to],
date: new Date(tx.received_at), date: new Date(tx.received_at),
}) extra: {},
}
ops.push(op)
} }
if (receiving) { if (receiving) {
ops.push({ const op: $Exact<Operation> = {
id: `${account.id}-${tx.hash}-IN`, id: `${account.id}-${tx.hash}-IN`,
hash: tx.hash, hash: tx.hash,
type: 'IN', type: 'IN',
@ -99,7 +102,9 @@ const txToOps = (account: Account) => (tx: Tx): Operation[] => {
senders: [tx.from], senders: [tx.from],
recipients: [tx.to], recipients: [tx.to],
date: new Date(new Date(tx.received_at) + 1), // hack: make the IN appear after the OUT in history. date: new Date(new Date(tx.received_at) + 1), // hack: make the IN appear after the OUT in history.
}) extra: {},
}
ops.push(op)
} }
return ops return ops
} }
@ -168,7 +173,7 @@ const signAndBroadcast = async ({
const hash = await api.broadcastTransaction(transaction) const hash = await api.broadcastTransaction(transaction)
onOperationBroadcasted({ const op: $Exact<Operation> = {
id: `${a.id}-${hash}-OUT`, id: `${a.id}-${hash}-OUT`,
hash, hash,
type: 'OUT', type: 'OUT',
@ -181,7 +186,10 @@ const signAndBroadcast = async ({
recipients: [t.recipient], recipients: [t.recipient],
transactionSequenceNumber: nonce, transactionSequenceNumber: nonce,
date: new Date(), date: new Date(),
}) extra: {},
}
onOperationBroadcasted(op)
} }
} }
@ -304,7 +312,8 @@ const EthereumBridge: WalletBridge<Transaction> = {
let emptyCount = 0 let emptyCount = 0
const mandatoryEmptyAccountSkip = getMandatoryEmptyAccountSkip(derivationMode) const mandatoryEmptyAccountSkip = getMandatoryEmptyAccountSkip(derivationMode)
const derivationScheme = getDerivationScheme({ derivationMode, currency }) const derivationScheme = getDerivationScheme({ derivationMode, currency })
for (let index = 0; index < 255; index++) { const stopAt = isIterableDerivationMode(derivationMode) ? 255 : 1
for (let index = 0; index < stopAt; index++) {
const freshAddressPath = runDerivationScheme(derivationScheme, currency, { const freshAddressPath = runDerivationScheme(derivationScheme, currency, {
account: index, account: index,
}) })

1
src/bridge/LibcoreBridge.js

@ -220,6 +220,7 @@ const LibcoreBridge: WalletBridge<Transaction> = {
.send({ .send({
accountId: account.id, accountId: account.id,
currencyId: account.currency.id, currencyId: account.currency.id,
blockHeight: account.blockHeight,
xpub: account.xpub || '', // FIXME only reason is to build the op id. we need to consider another id for making op id. xpub: account.xpub || '', // FIXME only reason is to build the op id. we need to consider another id for making op id.
derivationMode: account.derivationMode, derivationMode: account.derivationMode,
seedIdentifier: account.seedIdentifier, seedIdentifier: account.seedIdentifier,

11
src/bridge/RippleJSBridge.js

@ -11,6 +11,7 @@ import {
getDerivationModesForCurrency, getDerivationModesForCurrency,
getDerivationScheme, getDerivationScheme,
runDerivationScheme, runDerivationScheme,
isIterableDerivationMode,
} from '@ledgerhq/live-common/lib/derivation' } from '@ledgerhq/live-common/lib/derivation'
import { import {
getAccountPlaceholderName, getAccountPlaceholderName,
@ -104,7 +105,7 @@ async function signAndBroadcast({ a, t, deviceId, isCancelled, onSigned, onOpera
const hash = computeBinaryTransactionHash(transaction) const hash = computeBinaryTransactionHash(transaction)
onOperationBroadcasted({ const op: $Exact<Operation> = {
id: `${a.id}-${hash}-OUT`, id: `${a.id}-${hash}-OUT`,
hash, hash,
accountId: a.id, accountId: a.id,
@ -120,7 +121,9 @@ async function signAndBroadcast({ a, t, deviceId, isCancelled, onSigned, onOpera
transactionSequenceNumber: transactionSequenceNumber:
(a.operations.length > 0 ? a.operations[0].transactionSequenceNumber : 0) + (a.operations.length > 0 ? a.operations[0].transactionSequenceNumber : 0) +
a.pendingOperations.length, a.pendingOperations.length,
}) extra: {},
}
onOperationBroadcasted(op)
} }
} finally { } finally {
api.disconnect() api.disconnect()
@ -230,6 +233,7 @@ const txToOperation = (account: Account) => ({
recipients: [destination.address], recipients: [destination.address],
date: new Date(timestamp), date: new Date(timestamp),
transactionSequenceNumber: sequence, transactionSequenceNumber: sequence,
extra: {},
} }
return op return op
} }
@ -299,7 +303,8 @@ const RippleJSBridge: WalletBridge<Transaction> = {
const derivationModes = getDerivationModesForCurrency(currency) const derivationModes = getDerivationModesForCurrency(currency)
for (const derivationMode of derivationModes) { for (const derivationMode of derivationModes) {
const derivationScheme = getDerivationScheme({ derivationMode, currency }) const derivationScheme = getDerivationScheme({ derivationMode, currency })
for (let index = 0; index < 255; index++) { const stopAt = isIterableDerivationMode(derivationMode) ? 255 : 1
for (let index = 0; index < stopAt; index++) {
const freshAddressPath = runDerivationScheme(derivationScheme, currency, { const freshAddressPath = runDerivationScheme(derivationScheme, currency, {
account: index, account: index,
}) })

4
src/commands/libcoreGetFees.js

@ -6,7 +6,7 @@ import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc' import { createCommand, Command } from 'helpers/ipc'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { getWalletName } from '@ledgerhq/live-common/lib/account' import { getWalletName } from '@ledgerhq/live-common/lib/account'
import type { Account } from '@ledgerhq/live-common/lib/types' import type { Account, DerivationMode } from '@ledgerhq/live-common/lib/types'
import { import {
isValidAddress, isValidAddress,
libcoreAmountToBigNumber, libcoreAmountToBigNumber,
@ -26,7 +26,7 @@ type Input = {
accountIndex: number, accountIndex: number,
transaction: BitcoinLikeTransaction, transaction: BitcoinLikeTransaction,
currencyId: string, currencyId: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
} }

4
src/commands/libcoreScanFromXPUB.js

@ -1,7 +1,7 @@
// @flow // @flow
import { fromPromise } from 'rxjs/observable/fromPromise' import { fromPromise } from 'rxjs/observable/fromPromise'
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' import type { AccountRaw, DerivationMode } from '@ledgerhq/live-common/lib/types'
import { createCommand, Command } from 'helpers/ipc' import { createCommand, Command } from 'helpers/ipc'
import withLibcore from 'helpers/withLibcore' import withLibcore from 'helpers/withLibcore'
@ -10,7 +10,7 @@ import { scanAccountsFromXPUB } from 'helpers/libcore'
type Input = { type Input = {
currencyId: string, currencyId: string,
xpub: string, xpub: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
} }

49
src/commands/libcoreSignAndBroadcast.js

@ -2,17 +2,19 @@
import logger from 'logger' import logger from 'logger'
import { BigNumber } from 'bignumber.js' import { BigNumber } from 'bignumber.js'
import { StatusCodes } from '@ledgerhq/hw-transport'
import Btc from '@ledgerhq/hw-app-btc' import Btc from '@ledgerhq/hw-app-btc'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation' import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import type { OperationRaw, CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { OperationRaw, DerivationMode, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { getWalletName } from '@ledgerhq/live-common/lib/account' import { getWalletName } from '@ledgerhq/live-common/lib/account'
import { import {
libcoreAmountToBigNumber, libcoreAmountToBigNumber,
bigNumberToLibcoreAmount, bigNumberToLibcoreAmount,
getOrCreateWallet, getOrCreateWallet,
} from 'helpers/libcore' } from 'helpers/libcore'
import { UpdateYourApp } from 'config/errors'
import withLibcore from 'helpers/withLibcore' import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc' import { createCommand, Command } from 'helpers/ipc'
@ -26,8 +28,9 @@ type BitcoinLikeTransaction = {
type Input = { type Input = {
accountId: string, accountId: string,
blockHeight: number,
currencyId: string, currencyId: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
xpub: string, xpub: string,
index: number, index: number,
@ -41,7 +44,17 @@ type Result = { type: 'signed' } | { type: 'broadcasted', operation: OperationRa
const cmd: Command<Input, Result> = createCommand( const cmd: Command<Input, Result> = createCommand(
'libcoreSignAndBroadcast', 'libcoreSignAndBroadcast',
({ accountId, currencyId, derivationMode, seedIdentifier, xpub, index, transaction, deviceId }) => ({
accountId,
blockHeight,
currencyId,
derivationMode,
seedIdentifier,
xpub,
index,
transaction,
deviceId,
}) =>
Observable.create(o => { Observable.create(o => {
let unsubscribed = false let unsubscribed = false
const currency = getCryptoCurrencyById(currencyId) const currency = getCryptoCurrencyById(currencyId)
@ -50,6 +63,7 @@ const cmd: Command<Input, Result> = createCommand(
doSignAndBroadcast({ doSignAndBroadcast({
accountId, accountId,
currency, currency,
blockHeight,
derivationMode, derivationMode,
seedIdentifier, seedIdentifier,
xpub, xpub,
@ -79,6 +93,7 @@ const cmd: Command<Input, Result> = createCommand(
async function signTransaction({ async function signTransaction({
hwApp, hwApp,
currency, currency,
blockHeight,
transaction, transaction,
derivationMode, derivationMode,
sigHashType, sigHashType,
@ -86,15 +101,21 @@ async function signTransaction({
}: { }: {
hwApp: Btc, hwApp: Btc,
currency: CryptoCurrency, currency: CryptoCurrency,
blockHeight: number,
transaction: *, transaction: *,
derivationMode: string, derivationMode: DerivationMode,
sigHashType: number, sigHashType: number,
hasTimestamp: boolean, hasTimestamp: boolean,
}) { }) {
const additionals = [] const additionals = []
let expiryHeight let expiryHeight
if (currency.id === 'bitcoin_cash' || currency.id === 'bitcoin_gold') additionals.push('bip143') if (currency.id === 'bitcoin_cash' || currency.id === 'bitcoin_gold') additionals.push('bip143')
if (currency.id === 'zcash') expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00]) if (currency.id === 'zcash') {
expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00])
if (blockHeight >= 419200) {
additionals.push('sapling')
}
}
const rawInputs = transaction.getInputs() const rawInputs = transaction.getInputs()
const hasExtraData = currency.id === 'zcash' const hasExtraData = currency.id === 'zcash'
@ -166,6 +187,7 @@ async function signTransaction({
export async function doSignAndBroadcast({ export async function doSignAndBroadcast({
accountId, accountId,
derivationMode, derivationMode,
blockHeight,
seedIdentifier, seedIdentifier,
currency, currency,
xpub, xpub,
@ -178,8 +200,9 @@ export async function doSignAndBroadcast({
onOperationBroadcasted, onOperationBroadcasted,
}: { }: {
accountId: string, accountId: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
blockHeight: number,
currency: CryptoCurrency, currency: CryptoCurrency,
xpub: string, xpub: string,
index: number, index: number,
@ -222,12 +245,18 @@ export async function doSignAndBroadcast({
signTransaction({ signTransaction({
hwApp: new Btc(transport), hwApp: new Btc(transport),
currency, currency,
blockHeight,
transaction: builded, transaction: builded,
sigHashType: parseInt(sigHashType, 16), sigHashType: parseInt(sigHashType, 16),
hasTimestamp, hasTimestamp,
derivationMode, derivationMode,
}), }),
) ).catch(e => {
if (e && e.statusCode === StatusCodes.INCORRECT_P1_P2) {
throw new UpdateYourApp(`UpdateYourApp ${currency.id}`, currency)
}
throw e
})
if (!signedTransaction || isCancelled() || !njsAccount) return if (!signedTransaction || isCancelled() || !njsAccount) return
onSigned() onSigned()
@ -251,7 +280,7 @@ export async function doSignAndBroadcast({
const fee = libcoreAmountToBigNumber(builded.getFees()) const fee = libcoreAmountToBigNumber(builded.getFees())
// NB we don't check isCancelled() because the broadcast is not cancellable now! // NB we don't check isCancelled() because the broadcast is not cancellable now!
onOperationBroadcasted({ const op: $Exact<OperationRaw> = {
id: `${xpub}-${txHash}-OUT`, id: `${xpub}-${txHash}-OUT`,
hash: txHash, hash: txHash,
type: 'OUT', type: 'OUT',
@ -265,7 +294,9 @@ export async function doSignAndBroadcast({
recipients, recipients,
accountId, accountId,
date: new Date().toISOString(), date: new Date().toISOString(),
}) extra: {},
}
onOperationBroadcasted(op)
} }
export default cmd export default cmd

4
src/commands/libcoreSyncAccount.js

@ -1,6 +1,6 @@
// @flow // @flow
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' import type { AccountRaw, DerivationMode } from '@ledgerhq/live-common/lib/types'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { fromPromise } from 'rxjs/observable/fromPromise' import { fromPromise } from 'rxjs/observable/fromPromise'
@ -12,7 +12,7 @@ type Input = {
accountId: string, accountId: string,
currencyId: string, currencyId: string,
xpub: string, xpub: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
index: number, index: number,
} }

304
src/components/DevToolsPage/AccountImporter.js

@ -1,15 +1,18 @@
// @flow // @flow
/* eslint-disable react/no-multi-comp */
import React, { PureComponent, Fragment } from 'react' import React, { PureComponent, Fragment } from 'react'
import invariant from 'invariant' import invariant from 'invariant'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import type { Currency, Account } from '@ledgerhq/live-common/lib/types' import type { Currency, Account, DerivationMode } from '@ledgerhq/live-common/lib/types'
import { decodeAccount } from 'reducers/accounts' import { decodeAccount } from 'reducers/accounts'
import { addAccount } from 'actions/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 Switch from 'components/base/Switch'
import Spinner from 'components/base/Spinner' import Spinner from 'components/base/Spinner'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
@ -32,26 +35,40 @@ type Props = {
addAccount: Account => void, addAccount: Account => void,
} }
const INITIAL_STATE = { type ImportableAccountType = {
status: 'idle', name: string,
currency: null, currency: Currency,
xpub: '', derivationMode: DerivationMode,
account: null, xpub: string,
isSegwit: true,
isUnsplit: false,
error: null,
} }
type State = { type State = {
status: string, status: string,
importableAccounts: ImportableAccountType[],
currency: ?Currency, currency: ?Currency,
xpub: string, xpub: string,
account: ?Account, name: string,
isSegwit: boolean, isSegwit: boolean,
isUnsplit: boolean, isUnsplit: boolean,
error: ?Error, error: ?Error,
} }
const INITIAL_STATE = {
status: 'idle',
currency: null,
xpub: '',
name: 'dev',
isSegwit: true,
isUnsplit: false,
error: null,
importableAccounts: [],
}
class AccountImporter extends PureComponent<Props, State> { class AccountImporter extends PureComponent<Props, State> {
state = INITIAL_STATE state = INITIAL_STATE
@ -67,142 +84,199 @@ class AccountImporter extends PureComponent<Props, State> {
onChangeXPUB = xpub => this.setState({ xpub }) onChangeXPUB = xpub => this.setState({ xpub })
onChangeSegwit = isSegwit => this.setState({ isSegwit }) onChangeSegwit = isSegwit => this.setState({ isSegwit })
onChangeUnsplit = isUnsplit => this.setState({ isUnsplit }) onChangeUnsplit = isUnsplit => this.setState({ isUnsplit })
onChangeName = name => this.setState({ name })
isValid = () => { isValid = () => {
const { currency, xpub } = this.state const { currency, xpub, status } = this.state
return !!currency && !!xpub return !!currency && !!xpub && status !== 'scanning'
} }
scan = async () => { scan = async () => {
if (!this.isValid()) return
this.setState({ status: 'scanning' }) this.setState({ status: 'scanning' })
const { importableAccounts } = this.state
try { try {
const { currency, xpub, isSegwit, isUnsplit } = this.state for (let i = 0; i < importableAccounts.length; i++) {
invariant(currency, 'no currency') const a = importableAccounts[i]
const derivationMode = isSegwit const scanPayload = {
? isUnsplit seedIdentifier: `dev_${a.xpub}`,
? 'segwit_unsplit' currencyId: a.currency.id,
: 'segwit' xpub: a.xpub,
: isUnsplit derivationMode: a.derivationMode,
? 'unsplit' }
: '' const rawAccount = await scanFromXPUB.send(scanPayload).toPromise()
const rawAccount = await scanFromXPUB const account = decodeAccount(rawAccount)
.send({ await this.import({
seedIdentifier: 'dev_tool', ...account,
currencyId: currency.id, name: a.name,
xpub,
derivationMode,
}) })
.toPromise() this.removeImportableAccount(a)
const account = decodeAccount(rawAccount) }
this.setState({ status: 'finish', account }) this.reset()
} catch (error) { } catch (error) {
this.setState({ status: 'error', error }) this.setState({ status: 'error', error })
} }
} }
import = async () => { addToScan = () => {
const { account } = this.state 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') invariant(account, 'no account')
await idleCallback() await idleCallback()
this.props.addAccount(account) this.props.addAccount(account)
this.reset()
} }
reset = () => this.setState(INITIAL_STATE) reset = () => this.setState(INITIAL_STATE)
render() { 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 const supportsSplit = !!currency && !!currency.forkedFrom
return ( return (
<Card title="Import from xpub" flow={3}> <Fragment>
{status === 'idle' ? ( <Card title="Import from xpub" flow={3}>
<Fragment> {status === 'idle' || status === 'scanning' ? (
<Box flow={1}> <Fragment>
<Label>{'currency'}</Label> <Box flow={1}>
<SelectCurrency autoFocus value={currency} onChange={this.onChangeCurrency} /> <Label>{'currency'}</Label>
</Box> <SelectCurrency autoFocus value={currency} onChange={this.onChangeCurrency} />
{currency && (currency.supportsSegwit || supportsSplit) ? ( </Box>
<Box horizontal justify="flex-end" align="center" flow={3}> {currency && (currency.supportsSegwit || supportsSplit) ? (
{supportsSplit && ( <Box horizontal justify="flex-end" align="center" flow={3}>
<Box horizontal align="center" flow={1}> {supportsSplit && (
<Box ff="Museo Sans|Bold" fontSize={4}> <Box horizontal align="center" flow={1}>
{'unsplit'} <Box ff="Museo Sans|Bold" fontSize={4}>
{'unsplit'}
</Box>
<Switch isChecked={isUnsplit} onChange={this.onChangeUnsplit} />
</Box> </Box>
<Switch isChecked={isUnsplit} onChange={this.onChangeUnsplit} /> )}
</Box> {currency.supportsSegwit && (
)} <Box horizontal align="center" flow={1}>
{currency.supportsSegwit && ( <Box ff="Museo Sans|Bold" fontSize={4}>
<Box horizontal align="center" flow={1}> {'segwit'}
<Box ff="Museo Sans|Bold" fontSize={4}> </Box>
{'segwit'} <Switch isChecked={isSegwit} onChange={this.onChangeSegwit} />
</Box> </Box>
<Switch isChecked={isSegwit} onChange={this.onChangeSegwit} /> )}
</Box>
)}
</Box>
) : null}
<Box flow={1}>
<Label>{'xpub'}</Label>
<Input
placeholder="xpub"
value={xpub}
onChange={this.onChangeXPUB}
onEnter={this.scan}
/>
</Box>
<Box align="flex-end">
<Button primary small disabled={!this.isValid()} onClick={this.scan}>
{'scan'}
</Button>
</Box>
</Fragment>
) : status === 'scanning' ? (
<Box align="center" justify="center" p={5}>
<Spinner size={16} />
</Box>
) : status === 'finish' ? (
account ? (
<Box p={8} align="center" justify="center" flow={5} horizontal>
<Box horizontal flow={4} color="graphite" align="center">
{currency && <CurrencyCircleIcon size={64} currency={currency} />}
<Box>
<Box ff="Museo Sans|Bold">{account.name}</Box>
<FormattedVal
fontSize={2}
alwaysShowSign={false}
color="graphite"
unit={account.unit}
showCode
val={account.balance || 0}
/>
<Box fontSize={2}>{`${account.operations.length} operation(s)`}</Box>
</Box> </Box>
) : null}
<Box flow={1}>
<Label>{'xpub'}</Label>
<Input
placeholder="xpub"
value={xpub}
onChange={this.onChangeXPUB}
onEnter={this.addToScan}
/>
</Box> </Box>
<Box flow={1}>
<Button outline small disabled={!account} onClick={this.import}> <Label>{'name'}</Label>
{'import'} <Input
</Button> placeholder="name"
</Box> value={name}
) : ( onChange={this.onChangeName}
onEnter={this.addToScan}
/>
</Box>
<Box align="flex-end">
<Button primary small disabled={!this.isValid()} onClick={this.addToScan}>
{'add to scan'}
</Button>
</Box>
</Fragment>
) : status === 'error' ? (
<Box align="center" justify="center" p={5} flow={4}> <Box align="center" justify="center" p={5} flow={4}>
<Box>{'No accounts found or wrong xpub'}</Box> <Box>
<TranslatedError error={error} />
</Box>
<Button primary onClick={this.reset} small autoFocus> <Button primary onClick={this.reset} small autoFocus>
{'Reset'} {'Reset'}
</Button> </Button>
</Box> </Box>
) ) : null}
) : status === 'error' ? ( </Card>
<Box align="center" justify="center" p={5} flow={4}> {!!importableAccounts.length && (
<Box> <Card flow={2}>
<TranslatedError error={error} /> {importableAccounts.map((acc, i) => (
</Box> <ImportableAccount
<Button primary onClick={this.reset} small autoFocus> key={acc.xpub}
{'Reset'} importableAccount={acc}
</Button> onRemove={this.removeImportableAccount}
</Box> isLoading={status === 'scanning' && i === 0}
) : null} >
</Card> {acc.xpub}
</ImportableAccount>
))}
{status !== 'scanning' && (
<Box mt={4} align="flex-start">
<Button primary onClick={this.scan}>
{'Launch scan'}
</Button>
</Box>
)}
</Card>
)}
</Fragment>
)
}
}
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 (
<Box horizontal flow={2} align="center">
{isLoading && <Spinner size={16} color="rgba(0, 0, 0, 0.3)" />}
<CurrencyCircleIcon currency={importableAccount.currency} size={24} />
<Box grow ff="Rubik" fontSize={3}>
<Ellipsis>{`[${importableAccount.name}] ${importableAccount.derivationMode ||
'default'} ${importableAccount.xpub}`}</Ellipsis>
</Box>
{!isLoading && (
<FakeLink onClick={this.remove} fontSize={3}>
{'Remove'}
</FakeLink>
)}
</Box>
) )
} }
} }

4
src/components/modals/AddAccounts/steps/03-step-import.js

@ -144,7 +144,9 @@ class StepImport extends PureComponent<StepProps> {
}) })
} }
}, },
complete: () => setScanStatus('finished'), complete: () => {
setScanStatus('finished')
},
error: err => { error: err => {
logger.critical(err) logger.critical(err)
setScanStatus('error', err) setScanStatus('error', err)

2
src/config/urls.js

@ -8,7 +8,7 @@ export const urls = {
// Ledger support // Ledger support
faq: 'https://support.ledgerwallet.com/hc/en-us', faq: 'https://support.ledgerwallet.com/hc/en-us',
terms: 'https://www.ledgerwallet.com/terms', terms: 'https://www.ledger.com/pages/terms-of-use-and-disclaimer',
noDeviceBuyNew: 'https://www.ledgerwallet.com/', noDeviceBuyNew: 'https://www.ledgerwallet.com/',
noDeviceTrackOrder: 'http://order.ledgerwallet.com/', noDeviceTrackOrder: 'http://order.ledgerwallet.com/',
noDeviceLearnMore: 'https://www.ledgerwallet.com/', noDeviceLearnMore: 'https://www.ledgerwallet.com/',

2
src/helpers/countervalues.js

@ -14,6 +14,7 @@ import {
import logger from 'logger' import logger from 'logger'
import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/currencies' import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/currencies'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import network from '../api/network'
const pairsSelector = createSelector( const pairsSelector = createSelector(
currenciesSelector, currenciesSelector,
@ -62,6 +63,7 @@ const CounterValues = createCounterValues({
pairsSelector, pairsSelector,
setExchangePairsAction, setExchangePairsAction,
addExtraPollingHooks, addExtraPollingHooks,
network,
}) })
let sortCache let sortCache

44
src/helpers/libcore.js

@ -11,6 +11,7 @@ import {
getDerivationScheme, getDerivationScheme,
isSegwitDerivationMode, isSegwitDerivationMode,
isUnsplitDerivationMode, isUnsplitDerivationMode,
isIterableDerivationMode,
} from '@ledgerhq/live-common/lib/derivation' } from '@ledgerhq/live-common/lib/derivation'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { import {
@ -26,6 +27,7 @@ import type {
AccountRaw, AccountRaw,
OperationRaw, OperationRaw,
OperationType, OperationType,
DerivationMode,
} from '@ledgerhq/live-common/lib/types' } from '@ledgerhq/live-common/lib/types'
import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc' import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc'
@ -89,7 +91,7 @@ async function scanAccountsOnDeviceBySegwit({
currency: CryptoCurrency, currency: CryptoCurrency,
onAccountScanned: AccountRaw => void, onAccountScanned: AccountRaw => void,
isUnsubscribed: () => boolean, isUnsubscribed: () => boolean,
derivationMode: string, derivationMode: DerivationMode,
showNewAccount: boolean, showNewAccount: boolean,
}): Promise<AccountRaw[]> { }): Promise<AccountRaw[]> {
const isSegwit = isSegwitDerivationMode(derivationMode) const isSegwit = isSegwitDerivationMode(derivationMode)
@ -111,7 +113,6 @@ async function scanAccountsOnDeviceBySegwit({
// retrieve or create the wallet // retrieve or create the wallet
const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode }) const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
const accountsCount = await wallet.getAccountCount()
// recursively scan all accounts on device on the given app // recursively scan all accounts on device on the given app
// new accounts will be created in sqlite, existing ones will be updated // new accounts will be created in sqlite, existing ones will be updated
@ -121,7 +122,6 @@ async function scanAccountsOnDeviceBySegwit({
walletName, walletName,
devicePath, devicePath,
currency, currency,
accountsCount,
accountIndex: 0, accountIndex: 0,
accounts: [], accounts: [],
onAccountScanned, onAccountScanned,
@ -201,8 +201,7 @@ async function scanNextAccount(props: {
devicePath: string, devicePath: string,
currency: CryptoCurrency, currency: CryptoCurrency,
seedIdentifier: string, seedIdentifier: string,
derivationMode: string, derivationMode: DerivationMode,
accountsCount: number,
accountIndex: number, accountIndex: number,
accounts: AccountRaw[], accounts: AccountRaw[],
onAccountScanned: AccountRaw => void, onAccountScanned: AccountRaw => void,
@ -215,7 +214,6 @@ async function scanNextAccount(props: {
walletName, walletName,
devicePath, devicePath,
currency, currency,
accountsCount,
accountIndex, accountIndex,
accounts, accounts,
onAccountScanned, onAccountScanned,
@ -225,13 +223,12 @@ async function scanNextAccount(props: {
isUnsubscribed, isUnsubscribed,
} = props } = props
// create account only if account has not been scanned yet let njsAccount
// if it has already been created, we just need to get it, and sync it try {
const hasBeenScanned = accountIndex < accountsCount njsAccount = await wallet.getAccount(accountIndex)
} catch (err) {
const njsAccount = hasBeenScanned njsAccount = await createAccount(wallet, devicePath)
? await wallet.getAccount(accountIndex) }
: await createAccount(wallet, devicePath)
if (isUnsubscribed()) return [] if (isUnsubscribed()) return []
@ -259,15 +256,15 @@ async function scanNextAccount(props: {
if (isUnsubscribed()) return [] if (isUnsubscribed()) return []
const isEmpty = ops.length === 0 const isLast = ops.length === 0 || !isIterableDerivationMode(derivationMode)
if (!isEmpty || showNewAccount) { if (!isLast || showNewAccount) {
onAccountScanned(account) onAccountScanned(account)
accounts.push(account) accounts.push(account)
} }
// returns if the current index points on an account with no ops // returns if the current index points on an account with no ops
if (isEmpty) { if (isLast) {
return accounts return accounts
} }
@ -292,7 +289,7 @@ export async function getOrCreateWallet(
derivationMode, derivationMode,
}: { }: {
currency: CryptoCurrency, currency: CryptoCurrency,
derivationMode: string, derivationMode: DerivationMode,
}, },
): NJSWallet { ): NJSWallet {
const pool = core.getPoolInstance() const pool = core.getPoolInstance()
@ -335,7 +332,7 @@ async function buildAccountRaw({
seedIdentifier: string, seedIdentifier: string,
walletName: string, walletName: string,
currency: CryptoCurrency, currency: CryptoCurrency,
derivationMode: string, derivationMode: DerivationMode,
accountIndex: number, accountIndex: number,
core: *, core: *,
ops: NJSOperation[], ops: NJSOperation[],
@ -428,7 +425,7 @@ function buildOperationRaw({
core: *, core: *,
op: NJSOperation, op: NJSOperation,
xpub: string, xpub: string,
}): OperationRaw { }): $Exact<OperationRaw> {
const bitcoinLikeOperation = op.asBitcoinLikeOperation() const bitcoinLikeOperation = op.asBitcoinLikeOperation()
const bitcoinLikeTransaction = bitcoinLikeOperation.getTransaction() const bitcoinLikeTransaction = bitcoinLikeOperation.getTransaction()
const hash = bitcoinLikeTransaction.getHash() const hash = bitcoinLikeTransaction.getHash()
@ -462,6 +459,7 @@ function buildOperationRaw({
blockHash: null, blockHash: null,
accountId: xpub, // FIXME accountId: xpub !? accountId: xpub, // FIXME accountId: xpub !?
date: op.getDate().toISOString(), date: op.getDate().toISOString(),
extra: {},
} }
} }
@ -475,7 +473,7 @@ export async function syncAccount({
}: { }: {
core: *, core: *,
xpub: string, xpub: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
currency: CryptoCurrency, currency: CryptoCurrency,
index: number, index: number,
@ -551,20 +549,18 @@ export async function scanAccountsFromXPUB({
core: *, core: *,
currencyId: string, currencyId: string,
xpub: string, xpub: string,
derivationMode: string, derivationMode: DerivationMode,
seedIdentifier: string, seedIdentifier: string,
}) { }) {
const currency = getCryptoCurrencyById(currencyId) const currency = getCryptoCurrencyById(currencyId)
const walletName = getWalletName({ const walletName = getWalletName({
currency, currency,
seedIdentifier: 'debug', seedIdentifier,
derivationMode, derivationMode,
}) })
const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode }) const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
await wallet.eraseDataSince(new Date(0))
const index = 0 const index = 0
const isSegwit = isSegwitDerivationMode(derivationMode) const isSegwit = isSegwitDerivationMode(derivationMode)

1
src/internals/index.js

@ -1,4 +1,5 @@
// @flow // @flow
import '@babel/polyfill'
import commands from 'commands' import commands from 'commands'
import logger from 'logger' import logger from 'logger'
import uuid from 'uuid/v4' import uuid from 'uuid/v4'

6
static/i18n/en/app.json

@ -170,7 +170,7 @@
"luno": "Luno makes it safe and easy to buy, store and learn about cryptocurrencies like Bitcoin and Ethereum", "luno": "Luno makes it safe and easy to buy, store and learn about cryptocurrencies like Bitcoin and Ethereum",
"shapeshift": "ShapeShift is an online marketplace where users can buy and sell digital assets. It is a fast and secure way for the world to buy and sell digital assets, with no lengthy signup process, no counterparty risk, and no friction.", "shapeshift": "ShapeShift is an online marketplace where users can buy and sell digital assets. It is a fast and secure way for the world to buy and sell digital assets, with no lengthy signup process, no counterparty risk, and no friction.",
"genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP.", "genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP.",
"kyber": "KYBER, his a trading platform for exchange and conversion of ERC-20 tokens" "kyber": "Kyber is a trading platform for exchange and conversion of ERC-20 tokens"
}, },
"genuinecheck": { "genuinecheck": {
"modal": { "modal": {
@ -826,8 +826,8 @@
"description": "Please try again or contact Ledger Support" "description": "Please try again or contact Ledger Support"
}, },
"UpdateYourApp": { "UpdateYourApp": {
"title": "App update required. Uninstall and reinstall the {{managerAppName}} app in the Manager", "title": "App update required",
"description": null "description": "Uninstall and reinstall the {{managerAppName}} app in the Manager"
}, },
"WebsocketConnectionError": { "WebsocketConnectionError": {
"title": "Sorry, try again (websocket error).", "title": "Sorry, try again (websocket error).",

60
yarn.lock

@ -1670,10 +1670,10 @@
camelcase "^5.0.0" camelcase "^5.0.0"
prettier "^1.13.7" prettier "^1.13.7"
"@ledgerhq/hw-app-btc@4.24.0": "@ledgerhq/hw-app-btc@^4.24.0", "@ledgerhq/hw-app-btc@^4.27.0":
version "4.24.0" version "4.27.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.24.0.tgz#8889b2bc9b9583209ed24f832c96ea8d23e1dc74" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.27.0.tgz#11fc822bd34a47a39b1a7ae03ced69cf1d432796"
integrity sha512-OEc8UCcdAWp10PPM9Keoh8imuusmNVe2o/89ujMT5UIWOGCu7duezpsnCY11jGNxf2hyos6lezUMlUAOHBuISQ== integrity sha512-7Ck48wCBb6nd9UXarNeGOsOqbOTi2cs4AxFhbDNrVLvPiBSH0yEiNQEF95J6u5BxKkAdM1GV9LoRumR4KhZGqQ==
dependencies: dependencies:
"@ledgerhq/hw-transport" "^4.24.0" "@ledgerhq/hw-transport" "^4.24.0"
create-hash "^1.1.3" create-hash "^1.1.3"
@ -1701,6 +1701,14 @@
"@ledgerhq/hw-transport" "^4.24.0" "@ledgerhq/hw-transport" "^4.24.0"
bip32-path "0.4.2" bip32-path "0.4.2"
"@ledgerhq/hw-app-xrp@^4.25.0":
version "4.25.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-xrp/-/hw-app-xrp-4.25.0.tgz#d97d7e85290dd2d1ec99f85747a26145e7a7383e"
integrity sha512-kG9S8CxUFMG1hbLBiKtoPMquzlTigndDsxhoXB8oywAdbGsoCi2cufMHV2p5Bek6YlGfn5J5p49Hx5iNIf2y5Q==
dependencies:
"@ledgerhq/hw-transport" "^4.24.0"
bip32-path "0.4.2"
"@ledgerhq/hw-transport-node-hid@4.24.0": "@ledgerhq/hw-transport-node-hid@4.24.0":
version "4.24.0" version "4.24.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.24.0.tgz#8457969d66819e8f7f50d5dd96527ab26cd3787d" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.24.0.tgz#8457969d66819e8f7f50d5dd96527ab26cd3787d"
@ -1733,10 +1741,10 @@
dependencies: dependencies:
events "^3.0.0" events "^3.0.0"
"@ledgerhq/ledger-core@2.0.0-rc.8": "@ledgerhq/ledger-core@2.0.0-rc.11":
version "2.0.0-rc.8" version "2.0.0-rc.11"
resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.8.tgz#618ff2ca091464c71890678d3912921ee46f98af" resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.11.tgz#5b314e222f487dfa8f525ba1ef008ae30b289339"
integrity sha512-UYEwlw7+JfRCPw1z2kw9EGs88wsk5XBGxMPpNPF1cdpE7extEL63Gr+4h4ibU6kXEeEIpfI0lZ2l/6HwzMw6EQ== integrity sha512-HmtUd3WrVhJQtjNe6qO/hGrnzrE2YbdaTQnLhsQyD3qN1vUwHmanHjqOqVLFRI8a3KqVdMFqYMvn3N5c0hsLuQ==
dependencies: dependencies:
"@ledgerhq/hw-app-btc" "^4.7.3" "@ledgerhq/hw-app-btc" "^4.7.3"
"@ledgerhq/hw-transport-node-hid" "^4.7.6" "@ledgerhq/hw-transport-node-hid" "^4.7.6"
@ -1745,19 +1753,23 @@
bindings "^1.3.0" bindings "^1.3.0"
nan "^2.6.2" nan "^2.6.2"
"@ledgerhq/live-common@4.0.0-beta.1": "@ledgerhq/live-common@4.3.0":
version "4.0.0-beta.1" version "4.3.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-4.0.0-beta.1.tgz#52ed90757761a08a5f5c9c40c1fed0cb2f1fb4ca" resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-4.3.0.tgz#f983cb691642f1372d89e6a9320986141ab31045"
integrity sha512-Ms06Za/EI8yP/4GOAziCMecqIssZbkS5tjSCgaqd1h0zHLzM6i6PvgjhJyvG5SrET6+uQVp4YM4qbo2tc6irjQ== integrity sha512-086MHRLvkPB7qxpzBGm2W1bE3yMbUxwCF2JuEMiQzgTK4fNPYV9mz+qR+qhYYcUXuXZQ2fCnCBYDqUlfo1Wb3g==
dependencies: dependencies:
axios "^0.18.0" "@ledgerhq/hw-app-btc" "^4.24.0"
"@ledgerhq/hw-app-eth" "^4.24.0"
"@ledgerhq/hw-app-xrp" "^4.24.0"
"@ledgerhq/hw-transport" "^4.24.0"
bignumber.js "^7.2.1" bignumber.js "^7.2.1"
eip55 "^1.0.3"
invariant "^2.2.2" invariant "^2.2.2"
lodash "^4.17.4" lodash "^4.17.4"
node-lzw "^0.3.1" node-lzw "^0.3.1"
numeral "^2.0.6" numeral "^2.0.6"
prando "^3.0.1" prando "^3.0.1"
react "^16.4.0" react "*"
react-i18next "^8.0.7" react-i18next "^8.0.7"
react-redux "^5.0.7" react-redux "^5.0.7"
redux "^4.0.0" redux "^4.0.0"
@ -13342,15 +13354,15 @@ react-treebeard@^2.1.0:
shallowequal "^0.2.2" shallowequal "^0.2.2"
velocity-react "^1.3.1" velocity-react "^1.3.1"
react@^16.2.0, react@^16.4.0, react@^16.4.1: react@*, react@^16.2.0, react@^16.6.1:
version "16.4.1" version "16.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" resolved "https://registry.yarnpkg.com/react/-/react-16.6.1.tgz#ee2aef4f0a09e494594882029821049772f915fe"
integrity sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg== integrity sha512-OtawJThYlvRgm9BXK+xTL7BIlDx8vv21j+fbQDjRRUyok6y7NyjlweGorielTahLZHYIdKUoK2Dp9ByVWuMqxw==
dependencies: dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types "^15.6.0" prop-types "^15.6.2"
scheduler "^0.11.0"
reactcss@^1.2.0: reactcss@^1.2.0:
version "1.2.3" version "1.2.3"
@ -14249,6 +14261,14 @@ sax@^1.2.4, sax@~1.2.1, sax@~1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
scheduler@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.0.tgz#def1f1bfa6550cc57981a87106e65e8aea41a6b5"
integrity sha512-MAYbBfmiEHxF0W+c4CxMpEqMYK+rYF584VP/qMKSiHM6lTkBKKYOJaDiSILpJHla6hBOsVd6GucPL46o2Uq3sg==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^0.3.0: schema-utils@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"

Loading…
Cancel
Save