Browse Source

Merge pull request #1574 from gre/migrate-accounts

Migrate to new live-common major
gre-patch-1
Gaëtan Renaudeau 6 years ago
committed by GitHub
parent
commit
2d8318c35d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      package.json
  2. 2
      src/__mocks__/storybook-state.js
  3. 2
      src/actions/general.js
  4. 2
      src/api/Ripple.js
  5. 46
      src/bridge/EthereumJSBridge.js
  6. 10
      src/bridge/LibcoreBridge.js
  7. 39
      src/bridge/RippleJSBridge.js
  8. 4
      src/bridge/makeMockBridge.js
  9. 2
      src/commands/debugAppInfosForCurrency.js
  10. 2
      src/commands/getAddress.js
  11. 29
      src/commands/libcoreGetFees.js
  12. 8
      src/commands/libcoreScanFromXPUB.js
  13. 66
      src/commands/libcoreSignAndBroadcast.js
  14. 16
      src/commands/libcoreSyncAccount.js
  15. 2
      src/components/AccountPage/AccountHeaderActions.js
  16. 2
      src/components/AccountPage/index.js
  17. 2
      src/components/BalanceSummary/index.js
  18. 2
      src/components/BalanceSummary/stories.js
  19. 2
      src/components/CalculateBalance.js
  20. 2
      src/components/CounterValue/stories.js
  21. 2
      src/components/CurrentAddress/index.js
  22. 14
      src/components/DevToolsPage/AccountImporter.js
  23. 11
      src/components/EnsureDeviceApp.js
  24. 2
      src/components/OperationsList/AmountCell.js
  25. 2
      src/components/OperationsList/index.js
  26. 14
      src/components/QRCodeExporter.js
  27. 2
      src/components/RecipientAddress/index.js
  28. 2
      src/components/SettingsPage/CounterValueSelect.js
  29. 4
      src/components/SettingsPage/sections/Tools.js
  30. 2
      src/components/base/Chart/handleMouseEvents.js
  31. 2
      src/components/base/Chart/stories.js
  32. 5
      src/components/base/FlipTicker/stories.js
  33. 2
      src/components/base/FormattedVal/index.js
  34. 2
      src/components/base/InputCurrency/index.js
  35. 2
      src/components/base/InputCurrency/stories.js
  36. 31
      src/components/base/QRCode/index.js
  37. 2
      src/components/modals/AccountSettingRenderBody.js
  38. 2
      src/components/modals/AddAccounts/index.js
  39. 2
      src/components/modals/AddAccounts/steps/03-step-import.js
  40. 19
      src/components/modals/Debug.js
  41. 2
      src/components/modals/OperationDetails.js
  42. 4
      src/components/modals/Receive/steps/04-step-receive-funds.js
  43. 11
      src/config/cryptocurrencies.js
  44. 22
      src/helpers/accountId.js
  45. 144
      src/helpers/accountModel.js
  46. 20
      src/helpers/accountName.js
  47. 38
      src/helpers/accountOrdering.js
  48. 16
      src/helpers/bip32.js
  49. 2
      src/helpers/countervalues.js
  50. 38
      src/helpers/derivations.js
  51. 10
      src/helpers/isAccountEmpty.js
  52. 277
      src/helpers/libcore.js
  53. 4
      src/reducers/accounts.js
  54. 2
      src/reducers/settings.js
  55. 134
      yarn.lock

3
package.json

@ -41,7 +41,7 @@
"@ledgerhq/hw-transport": "^4.24.0",
"@ledgerhq/hw-transport-node-hid": "4.24.0",
"@ledgerhq/ledger-core": "2.0.0-rc.8",
"@ledgerhq/live-common": "3.8.0",
"@ledgerhq/live-common": "4.0.0-beta.1",
"animated": "^0.2.2",
"async": "^2.6.1",
"axios": "^0.18.0",
@ -70,6 +70,7 @@
"measure-scrollbar": "^1.1.0",
"moment": "^2.22.2",
"qrcode": "^1.2.0",
"qrloop": "^0.6.1",
"qs": "^6.5.1",
"raven": "^2.5.0",
"raven-js": "^3.24.2",

2
src/__mocks__/storybook-state.js

@ -2,7 +2,7 @@ import { genStoreState } from '@ledgerhq/live-common/lib/countervalues/mock'
import {
getCryptoCurrencyById,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
} from '@ledgerhq/live-common/lib/currencies'
export default {
countervalues: genStoreState([

2
src/actions/general.js

@ -8,7 +8,7 @@ import {
getOrderAccounts,
} from 'reducers/settings'
import { accountsSelector } from 'reducers/accounts'
import { sortAccounts } from 'helpers/accountOrdering'
import { sortAccounts } from '@ledgerhq/live-common/lib/account'
const accountsBtcBalanceSelector = createSelector(
accountsSelector,

2
src/api/Ripple.js

@ -6,7 +6,7 @@ import {
parseCurrencyUnit,
getCryptoCurrencyById,
formatCurrencyUnit,
} from '@ledgerhq/live-common/lib/helpers/currencies'
} from '@ledgerhq/live-common/lib/currencies'
const rippleUnit = getCryptoCurrencyById('ripple').units[0]

46
src/bridge/EthereumJSBridge.js

@ -8,14 +8,22 @@ import AdvancedOptions from 'components/AdvancedOptions/EthereumKind'
import throttle from 'lodash/throttle'
import flatMap from 'lodash/flatMap'
import uniqBy from 'lodash/uniqBy'
import {
getDerivationModesForCurrency,
getDerivationScheme,
runDerivationScheme,
getMandatoryEmptyAccountSkip,
} from '@ledgerhq/live-common/lib/derivation'
import {
getAccountPlaceholderName,
getNewAccountPlaceholderName,
} from '@ledgerhq/live-common/lib/account'
import type { Account, Operation } from '@ledgerhq/live-common/lib/types'
import eip55 from 'eip55'
import { apiForCurrency } from 'api/Ethereum'
import type { Tx } from 'api/Ethereum'
import { getDerivations } from 'helpers/derivations'
import getAddressCommand from 'commands/getAddress'
import signTransactionCommand from 'commands/signTransaction'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { NotEnoughBalance, FeeNotLoaded, ETHAddressNonEIP } from 'config/errors'
import type { EditProps, WalletBridge } from './types'
@ -210,8 +218,8 @@ const EthereumBridge: WalletBridge<Transaction> = {
async function stepAddress(
index,
{ address, path: freshAddressPath, publicKey },
isStandard,
{ address, path: freshAddressPath },
derivationMode,
shouldSkipEmpty,
): { account?: Account, complete?: boolean } {
const balance = await api.getAccountBalance(address)
@ -222,19 +230,21 @@ const EthereumBridge: WalletBridge<Transaction> = {
if (finished) return { complete: true }
const freshAddress = address
const accountId = `ethereumjs:${currency.id}:${address}:${publicKey}`
const accountId = `ethereumjs:2:${currency.id}:${address}:${derivationMode}`
if (txs.length === 0 && balance.isZero()) {
// this is an empty account
if (isStandard) {
if (derivationMode === '') {
// is standard derivation
if (newAccountCount === 0) {
// first zero account will emit one account as opportunity to create a new account..
const account: $Exact<Account> = {
id: accountId,
xpub: '',
seedIdentifier: freshAddress,
freshAddress,
freshAddressPath,
name: getNewAccountPlaceholderName(currency, index),
derivationMode,
name: getNewAccountPlaceholderName({ currency, index, derivationMode }),
balance,
blockHeight: currentBlock.height,
index,
@ -258,10 +268,11 @@ const EthereumBridge: WalletBridge<Transaction> = {
const account: $Exact<Account> = {
id: accountId,
xpub: '',
seedIdentifier: freshAddress,
freshAddress,
freshAddressPath,
name: getAccountPlaceholderName(currency, index, !isStandard),
derivationMode,
name: getAccountPlaceholderName({ currency, index, derivationMode }),
balance,
blockHeight: currentBlock.height,
index,
@ -288,21 +299,22 @@ const EthereumBridge: WalletBridge<Transaction> = {
async function main() {
try {
const derivations = getDerivations(currency)
const last = derivations[derivations.length - 1]
for (const derivation of derivations) {
const isStandard = last === derivation
const derivationModes = getDerivationModesForCurrency(currency)
for (const derivationMode of derivationModes) {
let emptyCount = 0
const mandatoryEmptyAccountSkip = derivation.mandatoryEmptyAccountSkip || 0
const mandatoryEmptyAccountSkip = getMandatoryEmptyAccountSkip(derivationMode)
const derivationScheme = getDerivationScheme({ derivationMode, currency })
for (let index = 0; index < 255; index++) {
const freshAddressPath = derivation({ currency, x: index, segwit: false })
const freshAddressPath = runDerivationScheme(derivationScheme, currency, {
account: index,
})
const res = await getAddressCommand
.send({ currencyId: currency.id, devicePath: deviceId, path: freshAddressPath })
.toPromise()
const r = await stepAddress(
index,
res,
isStandard,
derivationMode,
emptyCount < mandatoryEmptyAccountSkip,
)
logger.log(

10
src/bridge/LibcoreBridge.js

@ -129,7 +129,9 @@ const LibcoreBridge: WalletBridge<Transaction> = {
libcoreSyncAccount
.send({
accountId: account.id,
freshAddressPath: account.freshAddressPath,
derivationMode: account.derivationMode,
xpub: account.xpub || '',
seedIdentifier: account.seedIdentifier,
index: account.index,
currencyId: account.currency.id,
})
@ -218,9 +220,9 @@ const LibcoreBridge: WalletBridge<Transaction> = {
.send({
accountId: account.id,
currencyId: account.currency.id,
xpub: account.xpub,
freshAddress: account.freshAddress,
freshAddressPath: account.freshAddressPath,
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,
seedIdentifier: account.seedIdentifier,
index: account.index,
transaction: serializeTransaction(transaction),
deviceId,

39
src/bridge/RippleJSBridge.js

@ -7,7 +7,15 @@ import bs58check from 'ripple-bs58check'
import { computeBinaryTransactionHash } from 'ripple-hashes'
import throttle from 'lodash/throttle'
import type { Account, Operation } from '@ledgerhq/live-common/lib/types'
import { getDerivations } from 'helpers/derivations'
import {
getDerivationModesForCurrency,
getDerivationScheme,
runDerivationScheme,
} from '@ledgerhq/live-common/lib/derivation'
import {
getAccountPlaceholderName,
getNewAccountPlaceholderName,
} from '@ledgerhq/live-common/lib/account'
import getAddress from 'commands/getAddress'
import signTransaction from 'commands/signTransaction'
import {
@ -19,7 +27,6 @@ import {
} from 'api/Ripple'
import FeesRippleKind from 'components/FeesField/RippleKind'
import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import {
NotEnoughBalance,
FeeNotLoaded,
@ -211,7 +218,7 @@ const txToOperation = (account: Account) => ({
}
const op: $Exact<Operation> = {
id,
id: `${account.id}-${id}-${type}`,
hash: id,
accountId: account.id,
type,
@ -289,17 +296,19 @@ const RippleJSBridge: WalletBridge<Transaction> = {
const minLedgerVersion = Number(ledgers[0])
const maxLedgerVersion = Number(ledgers[1])
const derivations = getDerivations(currency)
for (const derivation of derivations) {
const legacy = derivation !== derivations[derivations.length - 1]
const derivationModes = getDerivationModesForCurrency(currency)
for (const derivationMode of derivationModes) {
const derivationScheme = getDerivationScheme({ derivationMode, currency })
for (let index = 0; index < 255; index++) {
const freshAddressPath = derivation({ currency, x: index, segwit: false })
const { address, publicKey } = await await getAddress
const freshAddressPath = runDerivationScheme(derivationScheme, currency, {
account: index,
})
const { address } = await await getAddress
.send({ currencyId: currency.id, devicePath: deviceId, path: freshAddressPath })
.toPromise()
if (finished) return
const accountId = `ripplejs:${currency.id}:${address}:${publicKey}`
const accountId = `ripplejs:2:${currency.id}:${address}:${derivationMode}`
let info
try {
@ -316,11 +325,12 @@ const RippleJSBridge: WalletBridge<Transaction> = {
if (!info) {
// account does not exist in Ripple server
// we are generating a new account locally
if (!legacy) {
if (derivationMode === '') {
o.next({
id: accountId,
xpub: '',
name: getNewAccountPlaceholderName(currency, index),
seedIdentifier: freshAddress,
derivationMode,
name: getNewAccountPlaceholderName({ currency, index, derivationMode }),
freshAddress,
freshAddressPath,
balance: BigNumber(0),
@ -353,8 +363,9 @@ const RippleJSBridge: WalletBridge<Transaction> = {
const account: $Exact<Account> = {
id: accountId,
xpub: '',
name: getAccountPlaceholderName(currency, index, legacy),
seedIdentifier: freshAddress,
derivationMode,
name: getAccountPlaceholderName({ currency, index, derivationMode }),
freshAddress,
freshAddressPath,
balance,

4
src/bridge/makeMockBridge.js

@ -6,11 +6,11 @@ import {
genAddingOperationsInAccount,
genOperation,
} from '@ledgerhq/live-common/lib/mock/account'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/operation'
import Prando from 'prando'
import { BigNumber } from 'bignumber.js'
import type { Operation } from '@ledgerhq/live-common/lib/types'
import { validateNameEdition } from 'helpers/accountName'
import { validateNameEdition } from '@ledgerhq/live-common/lib/account'
import { MOCK_DATA_SEED } from 'config/constants'
import type { WalletBridge } from './types'

2
src/commands/debugAppInfosForCurrency.js

@ -1,6 +1,6 @@
// @flow
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'

2
src/commands/getAddress.js

@ -1,6 +1,6 @@
// @flow
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'

29
src/commands/libcoreGetFees.js

@ -4,17 +4,16 @@ import { Observable } from 'rxjs'
import { BigNumber } from 'bignumber.js'
import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { getWalletName } from '@ledgerhq/live-common/lib/account'
import type { Account } from '@ledgerhq/live-common/lib/types'
import * as accountIdHelper from 'helpers/accountId'
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
@ -24,22 +23,20 @@ type BitcoinLikeTransaction = {
}
type Input = {
accountId: string,
accountIndex: number,
transaction: BitcoinLikeTransaction,
currencyId: string,
isSegwit: boolean,
isUnsplit: boolean,
derivationMode: string,
seedIdentifier: string,
}
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]),
derivationMode: a.derivationMode,
seedIdentifier: a.seedIdentifier,
}
}
@ -47,17 +44,21 @@ type Result = { totalFees: string }
const cmd: Command<Input, Result> = createCommand(
'libcoreGetFees',
({ accountId, currencyId, isSegwit, isUnsplit, accountIndex, transaction }) =>
({ currencyId, derivationMode, seedIdentifier, accountIndex, transaction }) =>
Observable.create(o => {
let unsubscribed = false
const isCancelled = () => unsubscribed
const currency = getCryptoCurrencyById(currencyId)
withLibcore(async core => {
const { walletName } = accountIdHelper.decode(accountId)
const walletName = getWalletName({
currency,
derivationMode,
seedIdentifier,
})
const njsWallet = await getOrCreateWallet(core, walletName, {
currencyId,
isSegwit,
isUnsplit,
currency,
derivationMode,
})
if (isCancelled()) return
const njsAccount = await njsWallet.getAccount(accountIndex)

8
src/commands/libcoreScanFromXPUB.js

@ -10,18 +10,18 @@ import { scanAccountsFromXPUB } from 'helpers/libcore'
type Input = {
currencyId: string,
xpub: string,
isSegwit: boolean,
isUnsplit: boolean,
derivationMode: string,
seedIdentifier: string,
}
type Result = AccountRaw
const cmd: Command<Input, Result> = createCommand(
'libcoreScanFromXPUB',
({ currencyId, xpub, isSegwit, isUnsplit }) =>
({ currencyId, xpub, derivationMode, seedIdentifier }) =>
fromPromise(
withLibcore(async core =>
scanAccountsFromXPUB({ core, currencyId, xpub, isSegwit, isUnsplit }),
scanAccountsFromXPUB({ core, currencyId, xpub, derivationMode, seedIdentifier }),
),
),
)

66
src/commands/libcoreSignAndBroadcast.js

@ -2,22 +2,21 @@
import logger from 'logger'
import { BigNumber } from 'bignumber.js'
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, isUnsplitPath } from 'helpers/bip32'
import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import type { OperationRaw, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { getWalletName } from '@ledgerhq/live-common/lib/account'
import {
libcoreAmountToBigNumber,
bigNumberToLibcoreAmount,
getOrCreateWallet,
} from 'helpers/libcore'
import { splittedCurrencies } from 'config/cryptocurrencies'
import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc'
import { withDevice } from 'helpers/deviceAccess'
import * as accountIdHelper from 'helpers/accountId'
type BitcoinLikeTransaction = {
amount: string,
@ -28,9 +27,9 @@ type BitcoinLikeTransaction = {
type Input = {
accountId: string,
currencyId: string,
derivationMode: string,
seedIdentifier: string,
xpub: string,
freshAddress: string,
freshAddressPath: string,
index: number,
transaction: BitcoinLikeTransaction,
deviceId: string,
@ -42,17 +41,18 @@ type Result = { type: 'signed' } | { type: 'broadcasted', operation: OperationRa
const cmd: Command<Input, Result> = createCommand(
'libcoreSignAndBroadcast',
({ accountId, currencyId, xpub, freshAddress, freshAddressPath, index, transaction, deviceId }) =>
({ accountId, currencyId, derivationMode, seedIdentifier, xpub, index, transaction, deviceId }) =>
Observable.create(o => {
let unsubscribed = false
const currency = getCryptoCurrencyById(currencyId)
const isCancelled = () => unsubscribed
withLibcore(core =>
doSignAndBroadcast({
accountId,
currencyId,
currency,
derivationMode,
seedIdentifier,
xpub,
freshAddress,
freshAddressPath,
index,
transaction,
deviceId,
@ -78,28 +78,26 @@ const cmd: Command<Input, Result> = createCommand(
async function signTransaction({
hwApp,
currencyId,
currency,
transaction,
derivationMode,
sigHashType,
supportsSegwit,
isSegwit,
hasTimestamp,
}: {
hwApp: Btc,
currencyId: string,
currency: CryptoCurrency,
transaction: *,
derivationMode: string,
sigHashType: number,
supportsSegwit: boolean,
isSegwit: boolean,
hasTimestamp: boolean,
}) {
const additionals = []
let expiryHeight
if (currencyId === 'bitcoin_cash' || currencyId === 'bitcoin_gold') additionals.push('bip143')
if (currencyId === 'zcash') expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00])
if (currency.id === 'bitcoin_cash' || currency.id === 'bitcoin_gold') additionals.push('bip143')
if (currency.id === 'zcash') expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00])
const rawInputs = transaction.getInputs()
const hasExtraData = currencyId === 'zcash'
const hasExtraData = currency.id === 'zcash'
const inputs = await Promise.all(
rawInputs.map(async input => {
@ -107,7 +105,7 @@ async function signTransaction({
const hexPreviousTransaction = Buffer.from(rawPreviousTransaction).toString('hex')
const previousTransaction = hwApp.splitTransaction(
hexPreviousTransaction,
supportsSegwit,
currency.supportsSegwit,
hasTimestamp,
hasExtraData,
)
@ -156,7 +154,7 @@ async function signTransaction({
outputScriptHex,
lockTime,
sigHashType,
isSegwit,
isSegwitDerivationMode(derivationMode),
initialTimestamp,
additionals,
expiryHeight,
@ -167,9 +165,10 @@ async function signTransaction({
export async function doSignAndBroadcast({
accountId,
currencyId,
derivationMode,
seedIdentifier,
currency,
xpub,
freshAddressPath,
index,
transaction,
deviceId,
@ -179,10 +178,10 @@ export async function doSignAndBroadcast({
onOperationBroadcasted,
}: {
accountId: string,
currencyId: string,
derivationMode: string,
seedIdentifier: string,
currency: CryptoCurrency,
xpub: string,
freshAddress: string,
freshAddressPath: string,
index: number,
transaction: BitcoinLikeTransaction,
deviceId: string,
@ -191,11 +190,9 @@ export async function doSignAndBroadcast({
onSigned: () => void,
onOperationBroadcasted: (optimisticOp: $Exact<OperationRaw>) => void,
}): Promise<void> {
const { walletName } = accountIdHelper.decode(accountId)
const walletName = getWalletName({ currency, seedIdentifier, derivationMode })
const isSegwit = isSegwitPath(freshAddressPath)
const isUnsplit = isUnsplitPath(freshAddressPath, splittedCurrencies[currencyId])
const njsWallet = await getOrCreateWallet(core, walletName, { currencyId, isSegwit, isUnsplit })
const njsWallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
if (isCancelled()) return
const njsAccount = await njsWallet.getAccount(index)
if (isCancelled()) return
@ -221,17 +218,14 @@ export async function doSignAndBroadcast({
const hasTimestamp = !!njsWalletCurrency.bitcoinLikeNetworkParameters.UsesTimestampedTransaction
// TODO: const timestampDelay = njsWalletCurrency.bitcoinLikeNetworkParameters.TimestampDelay
const currency = getCryptoCurrencyById(currencyId)
const signedTransaction = await withDevice(deviceId)(async transport =>
signTransaction({
hwApp: new Btc(transport),
currencyId,
currency,
transaction: builded,
sigHashType: parseInt(sigHashType, 16),
supportsSegwit: !!currency.supportsSegwit,
isSegwit: isSegwitPath(freshAddressPath),
hasTimestamp,
derivationMode,
}),
)

16
src/commands/libcoreSyncAccount.js

@ -1,6 +1,7 @@
// @flow
import type { AccountRaw } from '@ledgerhq/live-common/lib/types'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { fromPromise } from 'rxjs/observable/fromPromise'
import { createCommand, Command } from 'helpers/ipc'
@ -9,15 +10,24 @@ import withLibcore from 'helpers/withLibcore'
type Input = {
accountId: string,
freshAddressPath: string,
currencyId: string,
xpub: string,
derivationMode: string,
seedIdentifier: string,
index: number,
}
type Result = { rawAccount: AccountRaw, requiresCacheFlush: boolean }
const cmd: Command<Input, Result> = createCommand('libcoreSyncAccount', accountInfos =>
fromPromise(withLibcore(core => syncAccount({ ...accountInfos, core }))),
const cmd: Command<Input, Result> = createCommand(
'libcoreSyncAccount',
({ currencyId, ...accountInfos }) =>
fromPromise(
withLibcore(core => {
const currency = getCryptoCurrencyById(currencyId)
return syncAccount({ ...accountInfos, currency, core })
}),
),
)
export default cmd

2
src/components/AccountPage/AccountHeaderActions.js

@ -7,7 +7,7 @@ import { translate } from 'react-i18next'
import styled from 'styled-components'
import type { Account } from '@ledgerhq/live-common/lib/types'
import Tooltip from 'components/base/Tooltip'
import isAccountEmpty from 'helpers/isAccountEmpty'
import { isAccountEmpty } from '@ledgerhq/live-common/lib/account'
import { MODAL_SEND, MODAL_RECEIVE, MODAL_SETTINGS_ACCOUNT } from 'config/constants'

2
src/components/AccountPage/index.js

@ -8,7 +8,7 @@ import { Redirect } from 'react-router'
import type { Currency, Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import { accountSelector } from 'reducers/accounts'
import isAccountEmpty from 'helpers/isAccountEmpty'
import { isAccountEmpty } from '@ledgerhq/live-common/lib/account'
import {
counterValueCurrencySelector,
localeSelector,

2
src/components/BalanceSummary/index.js

@ -3,7 +3,7 @@
import React, { Fragment } from 'react'
import { BigNumber } from 'bignumber.js'
import moment from 'moment'
import { formatShort } from '@ledgerhq/live-common/lib/helpers/currencies'
import { formatShort } from '@ledgerhq/live-common/lib/currencies'
import type { Currency, Account } from '@ledgerhq/live-common/lib/types'
import Chart from 'components/base/Chart'

2
src/components/BalanceSummary/stories.js

@ -4,7 +4,7 @@ import React from 'react'
import { storiesOf } from '@storybook/react'
import { number } from '@storybook/addon-knobs'
import { translate } from 'react-i18next'
import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/currencies'
import BalanceInfos from './BalanceInfos'

2
src/components/CalculateBalance.js

@ -6,7 +6,7 @@ import { connect } from 'react-redux'
import { BigNumber } from 'bignumber.js'
import type { Account } from '@ledgerhq/live-common/lib/types'
import { getBalanceHistorySum } from '@ledgerhq/live-common/lib/helpers/account'
import { getBalanceHistorySum } from '@ledgerhq/live-common/lib/account'
import CounterValues from 'helpers/countervalues'
import {
exchangeSettingsForAccountSelector,

2
src/components/CounterValue/stories.js

@ -1,7 +1,7 @@
// @flow
import React from 'react'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { storiesOf } from '@storybook/react'
import { number } from '@storybook/addon-knobs'

2
src/components/CurrentAddress/index.js

@ -3,7 +3,7 @@
import React, { PureComponent } from 'react'
import { Trans, translate } from 'react-i18next'
import styled from 'styled-components'
import { encodeURIScheme } from '@ledgerhq/live-common/lib/helpers/currencies'
import { encodeURIScheme } from '@ledgerhq/live-common/lib/currencies'
import type { Account } from '@ledgerhq/live-common/lib/types'
import noop from 'lodash/noop'

14
src/components/DevToolsPage/AccountImporter.js

@ -21,7 +21,6 @@ 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'
@ -80,12 +79,19 @@ class AccountImporter extends PureComponent<Props, State> {
try {
const { currency, xpub, isSegwit, isUnsplit } = this.state
invariant(currency, 'no currency')
const derivationMode = isSegwit
? isUnsplit
? 'segwit_unsplit'
: 'segwit'
: isUnsplit
? 'unsplit'
: ''
const rawAccount = await scanFromXPUB
.send({
seedIdentifier: 'dev_tool',
currencyId: currency.id,
xpub,
isSegwit,
isUnsplit,
derivationMode,
})
.toPromise()
const account = decodeAccount(rawAccount)
@ -107,7 +113,7 @@ class AccountImporter extends PureComponent<Props, State> {
render() {
const { currency, xpub, isSegwit, isUnsplit, status, account, error } = this.state
const supportsSplit = !!currency && !!splittedCurrencies[currency.id]
const supportsSplit = !!currency && !!currency.forkedFrom
return (
<Card title="Import from xpub" flow={3}>
{status === 'idle' ? (

11
src/components/EnsureDeviceApp.js

@ -10,8 +10,11 @@ import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import logger from 'logger'
import getAddress from 'commands/getAddress'
import { createCancelablePolling } from 'helpers/promise'
import { standardDerivation } from 'helpers/derivations'
import { isSegwitPath } from 'helpers/bip32'
import {
isSegwitDerivationMode,
getDerivationScheme,
runDerivationScheme,
} from '@ledgerhq/live-common/lib/derivation'
import DeviceInteraction from 'components/DeviceInteraction'
import Text from 'components/base/Text'
@ -122,8 +125,8 @@ async function getAddressFromAccountOrCurrency(device, account, currency) {
currencyId: currency.id,
path: account
? account.freshAddressPath
: standardDerivation({ currency, segwit: false, x: 0 }),
segwit: account ? isSegwitPath(account.freshAddressPath) : false,
: runDerivationScheme(getDerivationScheme({ currency, derivationMode: '' }), currency),
segwit: account ? isSegwitDerivationMode(account.derivationMode) : false,
})
.toPromise()
return address

2
src/components/OperationsList/AmountCell.js

@ -2,7 +2,7 @@
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/operation'
import type { Currency, Unit, Operation } from '@ledgerhq/live-common/lib/types'
import Box from 'components/base/Box'
import CounterValue from 'components/CounterValue'

2
src/components/OperationsList/index.js

@ -8,7 +8,7 @@ import { translate } from 'react-i18next'
import {
groupAccountOperationsByDay,
groupAccountsOperationsByDay,
} from '@ledgerhq/live-common/lib/helpers/account'
} from '@ledgerhq/live-common/lib/account'
import type { Operation, Account } from '@ledgerhq/live-common/lib/types'

14
src/components/QRCodeExporter.js

@ -7,20 +7,24 @@ import { connect } from 'react-redux'
import { accountsSelector } from 'reducers/accounts'
import { exportSettingsSelector } from 'reducers/settings'
import { makeChunks } from '@ledgerhq/live-common/lib/bridgestream/exporter'
import { encode } from '@ledgerhq/live-common/lib/cross'
import { dataToFrames } from 'qrloop/exporter'
import QRCode from './base/QRCode'
const mapStateToProps = createSelector(
accountsSelector,
exportSettingsSelector,
(accounts, settings) => ({
chunks: makeChunks({
chunks: dataToFrames(
encode({
accounts,
settings,
exporterName: 'desktop',
exporterVersion: __APP_VERSION__,
chunkSize: 120,
}),
200,
4,
),
}),
)
@ -35,12 +39,12 @@ class QRCodeExporter extends PureComponent<
},
> {
static defaultProps = {
size: 440,
size: 460,
}
state = {
frame: 0,
fps: 5,
fps: 3,
}
componentDidMount() {

2
src/components/RecipientAddress/index.js

@ -4,7 +4,7 @@ import type { BigNumber } from 'bignumber.js'
import React, { PureComponent, Fragment } from 'react'
import styled from 'styled-components'
import noop from 'lodash/noop'
import { decodeURIScheme } from '@ledgerhq/live-common/lib/helpers/currencies'
import { decodeURIScheme } from '@ledgerhq/live-common/lib/currencies'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { radii } from 'styles/theme'

2
src/components/SettingsPage/CounterValueSelect.js

@ -3,7 +3,7 @@
import React, { Fragment, PureComponent } from 'react'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'
import { listFiatCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import { listFiatCurrencies } from '@ledgerhq/live-common/lib/currencies'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import { setCounterValue } from 'actions/settings'
import { counterValueCurrencySelector } from 'reducers/settings'

4
src/components/SettingsPage/sections/Tools.js

@ -25,9 +25,11 @@ class TabProfile extends PureComponent<*, *> {
renderQRCodeModal = ({ onClose }: any) => (
<ModalBody onClose={onClose} justify="center" align="center">
<ModalTitle>{'QRCode Mobile Export'}</ModalTitle>
<ModalContent flow={4}>
<ModalContent flow={2}>
<Box>Scan this animated QRCode with Ledger Live Mobile App</Box>
<Box flow={2}>
<QRCodeExporter />
</Box>
</ModalContent>
</ModalBody>
)

2
src/components/base/Chart/handleMouseEvents.js

@ -5,7 +5,7 @@ import * as d3 from 'd3'
import { renderToString } from 'react-dom/server'
import { ThemeProvider } from 'styled-components'
import { Provider } from 'react-redux'
import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/currencies'
import createStore from 'renderer/createStore'

2
src/components/base/Chart/stories.js

@ -2,7 +2,7 @@
import React, { Component, Fragment } from 'react'
import { BigNumber } from 'bignumber.js'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import Chance from 'chance'
import moment from 'moment'
import { storiesOf } from '@storybook/react'

5
src/components/base/FlipTicker/stories.js

@ -4,10 +4,7 @@ import React, { Component } from 'react'
import { BigNumber } from 'bignumber.js'
import { storiesOf } from '@storybook/react'
import {
formatCurrencyUnit,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
import { formatCurrencyUnit, getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/currencies'
import Chance from 'chance'
import Box from 'components/base/Box'

2
src/components/base/FormattedVal/index.js

@ -9,7 +9,7 @@ import { connect } from 'react-redux'
import type { Unit } from '@ledgerhq/live-common/lib/types'
import type { State } from 'reducers'
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies'
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/currencies'
import { marketIndicatorSelector, localeSelector } from 'reducers/settings'

2
src/components/base/InputCurrency/index.js

@ -4,7 +4,7 @@ import React, { PureComponent } from 'react'
import { BigNumber } from 'bignumber.js'
import uncontrollable from 'uncontrollable'
import styled from 'styled-components'
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies'
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/currencies'
import noop from 'lodash/noop'

2
src/components/base/InputCurrency/stories.js

@ -5,7 +5,7 @@ import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import { boolean } from '@storybook/addon-knobs'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import InputCurrency from 'components/base/InputCurrency'
const stories = storiesOf('Components', module)

31
src/components/base/QRCode/index.js

@ -23,22 +23,39 @@ class QRCode extends PureComponent<Props> {
this.drawQRCode()
}
_canvas = null
canvas = React.createRef()
drawQRCode() {
const { data, size, errorCorrectionLevel } = this.props
qrcode.toCanvas(this._canvas, data, {
width: size,
const { current } = this.canvas
if (!current) return
qrcode.toCanvas(
current,
data,
{
width: current.width,
margin: 0,
errorCorrectionLevel,
color: {
light: '#ffffff00', // transparent background
},
})
() => {
// fix again the CSS because lib changes it –_–
current.style.width = `${size}px`
current.style.height = `${size}px`
},
)
}
render() {
return <canvas style={{ cursor: 'none' }} ref={n => (this._canvas = n)} />
const { size } = this.props
const px = size * (window.devicePixelRatio || 1)
return (
<canvas
style={{ cursor: 'none', width: `${size}px`, height: `${size}px` }}
width={px}
height={px}
ref={this.canvas}
/>
)
}
}

2
src/components/modals/AccountSettingRenderBody.js

@ -10,7 +10,7 @@ import { translate } from 'react-i18next'
import type { Account, Unit, Currency } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import { MODAL_SETTINGS_ACCOUNT, MAX_ACCOUNT_NAME_SIZE } from 'config/constants'
import { validateNameEdition } from 'helpers/accountName'
import { validateNameEdition } from '@ledgerhq/live-common/lib/account'
import { updateAccount, removeAccount } from 'actions/accounts'
import { setDataModal } from 'reducers/modals'

2
src/components/modals/AddAccounts/index.js

@ -23,7 +23,7 @@ import { closeModal } from 'reducers/modals'
import Modal from 'components/base/Modal'
import Stepper from 'components/base/Stepper'
import { validateNameEdition } from 'helpers/accountName'
import { validateNameEdition } from '@ledgerhq/live-common/lib/account'
import StepChooseCurrency, { StepChooseCurrencyFooter } from './steps/01-step-choose-currency'
import StepConnectDevice, { StepConnectDeviceFooter } from './steps/02-step-connect-device'

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

@ -10,7 +10,7 @@ import uniq from 'lodash/uniq'
import { urls } from 'config/urls'
import ExternalLinkButton from 'components/base/ExternalLinkButton'
import RetryButton from 'components/base/RetryButton'
import isAccountEmpty from 'helpers/isAccountEmpty'
import { isAccountEmpty } from '@ledgerhq/live-common/lib/account'
import { getBridgeForCurrency } from 'bridge'

19
src/components/modals/Debug.js

@ -3,14 +3,13 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import last from 'lodash/last'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { getDerivationScheme, runDerivationScheme } from '@ledgerhq/live-common/lib/derivation'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
import { getCurrentDevice } from 'reducers/devices'
import Button from 'components/base/Button'
import Box from 'components/base/Box'
import Input from 'components/base/Input'
import { getDerivations } from 'helpers/derivations'
import getAddress from 'commands/getAddress'
import testInterval from 'commands/testInterval'
import testCrash from 'commands/testCrash'
@ -44,11 +43,14 @@ class Debug extends Component<*, *> {
onClickStressDevice = (device: *) => async () => {
try {
const currency = getCryptoCurrencyById('bitcoin')
const derivation = last(getDerivations(currency))
const derivationScheme = getDerivationScheme({
currency,
derivationMode: 'segwit',
})
for (let x = 0; x < 20; x++) {
const { address, path } = await getAddress
.send({
path: derivation({ currency, segwit: true, x }),
path: runDerivationScheme(derivationScheme, currency, { account: x }),
currencyId: currency.id,
devicePath: device.path,
})
@ -94,9 +96,12 @@ class Debug extends Component<*, *> {
.then(o => o.stringVersion),
)
const currency = getCryptoCurrencyById('bitcoin')
const derivation = last(getDerivations(currency))
const derivationScheme = getDerivationScheme({
currency,
derivationMode: 'segwit',
})
const obj = {
path: derivation({ currency, segwit: true, x: 0 }),
path: runDerivationScheme(derivationScheme, currency),
currencyId: currency.id,
devicePath: device.path,
}

2
src/components/modals/OperationDetails.js

@ -6,7 +6,7 @@ import { openURL } from 'helpers/linking'
import { translate } from 'react-i18next'
import styled from 'styled-components'
import moment from 'moment'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/operation'
import { getAccountOperationExplorer } from '@ledgerhq/live-common/lib/explorers'
import uniq from 'lodash/uniq'

4
src/components/modals/Receive/steps/04-step-receive-funds.js

@ -5,7 +5,7 @@ import React, { PureComponent } from 'react'
import TrackPage from 'analytics/TrackPage'
import getAddress from 'commands/getAddress'
import { isSegwitPath } from 'helpers/bip32'
import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation'
import Box from 'components/base/Box'
import CurrentAddressForAccount from 'components/CurrentAddressForAccount'
import { DisconnectedDevice, WrongDeviceForAccount } from 'config/errors'
@ -29,7 +29,7 @@ export default class StepReceiveFunds extends PureComponent<StepProps> {
currencyId: account.currency.id,
devicePath: device.path,
path: account.freshAddressPath,
segwit: isSegwitPath(account.freshAddressPath),
segwit: isSegwitDerivationMode(account.derivationMode),
verify: true,
}
const { address } = await getAddress.send(params).toPromise()

11
src/config/cryptocurrencies.js

@ -1,6 +1,6 @@
// @flow
import memoize from 'lodash/memoize'
import { listCryptoCurrencies as listCC } from '@ledgerhq/live-common/lib/helpers/currencies'
import { listCryptoCurrencies as listCC } from '@ledgerhq/live-common/lib/currencies'
import type { CryptoCurrencyIds } from '@ledgerhq/live-common/lib/types'
const supported: CryptoCurrencyIds[] = [
@ -35,12 +35,3 @@ 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,
},
}

22
src/helpers/accountId.js

@ -1,22 +0,0 @@
// @flow
import invariant from 'invariant'
type Params = {
type: string,
version: string,
xpub: string,
walletName: string,
}
export function encode({ type, version, xpub, walletName }: Params) {
return `${type}:${version}:${xpub}:${walletName}`
}
export function decode(accountId: string): Params {
invariant(typeof accountId === 'string', 'accountId is not a string')
const splitted = accountId.split(':')
invariant(splitted.length === 4, 'invalid size for accountId')
const [type, version, xpub, walletName] = splitted
return { type, version, xpub, walletName }
}

144
src/helpers/accountModel.js

@ -0,0 +1,144 @@
/**
* @module models/account
* @flow
*/
import { BigNumber } from 'bignumber.js'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import { createDataModel } from '@ledgerhq/live-common/lib/DataModel'
import type { DataModel } from '@ledgerhq/live-common/lib/DataModel'
import type { Account, AccountRaw, Operation } from '@ledgerhq/live-common/lib/types'
/**
* @memberof models/account
*/
export const opRetentionStategy = (maxDaysOld: number, keepFirst: number) => (
op: Operation,
index: number,
): boolean => index < keepFirst || Date.now() - op.date < 1000 * 60 * 60 * 24 * maxDaysOld
const opRetentionFilter = opRetentionStategy(366, 100)
const accountModel: DataModel<AccountRaw, Account> = createDataModel({
migrations: [
// 2018-10-10: change of the account id format to include the derivationMode and seedIdentifier in Account
raw => {
raw = { ...raw }
const { currencyId, freshAddressPath } = raw
const [type, originalVersion, xpubOrAddress, walletName] = raw.id.split(':')
let version = originalVersion
let derivationMode
let seedIdentifier
switch (type) {
case 'libcore': {
const i = walletName.indexOf('__') + currencyId.length + 1
derivationMode = walletName.slice(i + 2)
seedIdentifier = walletName.slice(0, i)
break
}
case 'ethereumjs': {
// reverse the derivation that was used to infer what was the derivationMode
if (freshAddressPath.match(/^44'\/60'\/0'\/[0-9]+$/)) {
derivationMode = 'ethM'
} else if (
currencyId === 'ethereum_classic' &&
freshAddressPath.match(/^44'\/60'\/160720'\/0'\/[0-9]+$/)
) {
derivationMode = 'etcM'
} else {
derivationMode = ''
}
delete raw.xpub
seedIdentifier = xpubOrAddress
version = '2' // replace version because no need to have the currencyId like used to do.
break
}
case 'ripplejs': {
// reverse the derivation that was used to infer what was the derivationMode
if (freshAddressPath.match(/^44'\/144'\/0'\/[0-9]+'$/)) {
derivationMode = 'rip'
} else {
derivationMode = ''
}
delete raw.xpub
seedIdentifier = xpubOrAddress
version = '2' // replace version because no need to have the currencyId like used to do.
break
}
default:
// this case should never happen
throw new Error(`unknown Account type=${type}`)
}
const id = `${type}:${version}:${currencyId}:${xpubOrAddress}:${derivationMode}`
return {
...raw,
id,
derivationMode,
seedIdentifier,
}
},
// ^- Each time a modification is brought to the model, add here a migration function here
],
decode: (rawAccount: AccountRaw): Account => {
const {
currencyId,
unitMagnitude,
operations,
pendingOperations,
lastSyncDate,
balance,
...acc
} = rawAccount
const currency = getCryptoCurrencyById(currencyId)
const unit = currency.units.find(u => u.magnitude === unitMagnitude) || currency.units[0]
const convertOperation = ({ date, value, fee, ...op }) => ({
...op,
accountId: acc.id,
date: new Date(date),
value: BigNumber(value),
fee: BigNumber(fee),
})
return {
...acc,
balance: BigNumber(balance),
operations: operations.map(convertOperation),
pendingOperations: pendingOperations.map(convertOperation),
unit,
currency,
lastSyncDate: new Date(lastSyncDate),
}
},
encode: ({
currency,
operations,
pendingOperations,
unit,
lastSyncDate,
balance,
...acc
}: Account): AccountRaw => {
const convertOperation = ({ date, value, fee, ...op }) => ({
...op,
date: date.toISOString(),
value: value.toString(),
fee: fee.toString(),
})
return {
...acc,
operations: operations.filter(opRetentionFilter).map(convertOperation),
pendingOperations: pendingOperations.map(convertOperation),
currencyId: currency.id,
unitMagnitude: unit.magnitude,
lastSyncDate: lastSyncDate.toISOString(),
balance: balance.toString(),
}
},
})
export default accountModel

20
src/helpers/accountName.js

@ -1,20 +0,0 @@
// @flow
import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { MAX_ACCOUNT_NAME_SIZE } from 'config/constants'
export const getAccountPlaceholderName = (
c: CryptoCurrency,
index: number,
isLegacy: boolean = false,
isUnsplit: boolean = false,
) => `${c.name} ${index + 1}${isLegacy ? ' (legacy)' : ''}${isUnsplit ? ' (unsplit)' : ''}`
export const getNewAccountPlaceholderName = getAccountPlaceholderName // same naming
// export const getNewAccountPlaceholderName = (_c: CryptoCurrency, _index: number) => `New Account`
export const validateNameEdition = (account: Account, name: ?string): string =>
(
(name || account.name || '').replace(/\s+/g, ' ').trim() ||
account.name ||
getAccountPlaceholderName(account.currency, account.index)
).slice(0, MAX_ACCOUNT_NAME_SIZE)

38
src/helpers/accountOrdering.js

@ -1,38 +0,0 @@
// @flow
import type { BigNumber } from 'bignumber.js'
import type { Account } from '@ledgerhq/live-common/lib/types'
type Param = {
accounts: Account[],
accountsBtcBalance: BigNumber[],
orderAccounts: string,
}
type SortMethod = 'name' | 'balance'
const sortMethod: { [_: SortMethod]: (Param) => string[] } = {
balance: ({ accounts, accountsBtcBalance }) =>
accounts
.map((a, i) => [a.id, accountsBtcBalance[i]])
.sort((a, b) => a[1].minus(b[1]).toNumber())
.map(o => o[0]),
name: ({ accounts }) =>
accounts
.slice(0)
.sort((a, b) => a.name.localeCompare(b.name))
.map(a => a.id),
}
export function sortAccounts(param: Param) {
const [order, sort] = param.orderAccounts.split('|')
if (order === 'name' || order === 'balance') {
const ids = sortMethod[order](param)
if (sort === 'asc') {
ids.reverse()
}
return ids
}
return null
}

16
src/helpers/bip32.js

@ -1,16 +0,0 @@
// @flow
type SplitConfig = {
coinType: number,
}
export const isSegwitPath = (path: string): boolean => path.startsWith("49'")
export const isUnsplitPath = (path: string, splitConfig: SplitConfig) => {
try {
const coinType = parseInt(path.split('/')[1], 10)
return coinType === splitConfig.coinType
} catch (e) {
return false
}
}

2
src/helpers/countervalues.js

@ -12,7 +12,7 @@ import {
intermediaryCurrency,
} from 'reducers/settings'
import logger from 'logger'
import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/currencies'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
const pairsSelector = createSelector(

38
src/helpers/derivations.js

@ -1,38 +0,0 @@
// @flow
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
type Derivation = {
({
currency: CryptoCurrency,
segwit: boolean,
x: number,
}): string,
mandatoryEmptyAccountSkip?: number,
}
const ethLegacyMEW: Derivation = ({ x }) => `44'/60'/0'/${x}`
ethLegacyMEW.mandatoryEmptyAccountSkip = 10
const etcLegacyMEW: Derivation = ({ x }) => `44'/60'/160720'/0'/${x}`
etcLegacyMEW.mandatoryEmptyAccountSkip = 10
const rippleLegacy: Derivation = ({ x }) => `44'/144'/0'/${x}'`
const legacyDerivations = {
ethereum: [ethLegacyMEW],
ethereum_classic: [ethLegacyMEW, etcLegacyMEW],
ripple: [rippleLegacy],
}
export const standardDerivation: Derivation = ({ currency, segwit, x }) => {
const purpose = segwit ? 49 : 44
const { coinType } = currency
return `${purpose}'/${coinType}'/${x}'/0/0`
}
// return an array of ways to derivate, by convention the latest is the standard one.
export const getDerivations = (currency: CryptoCurrency): Derivation[] => [
...(legacyDerivations[currency.id] || []),
standardDerivation,
]

10
src/helpers/isAccountEmpty.js

@ -1,10 +0,0 @@
// @flow
import type { Account } from '@ledgerhq/live-common/lib/types'
// TODO move this back in live-common
// An account is empty if there is no operations AND balance is zero.
// balance can be non-zero in edgecases, for instance:
// - Ethereum contract only funds (api limitations)
// - Ripple node that don't show all ledgers and if you have very old txs
export default (a: Account): boolean => a.operations.length === 0 && a.balance.isZero()

277
src/helpers/libcore.js

@ -6,18 +6,31 @@ import logger from 'logger'
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 {
getDerivationModesForCurrency,
getDerivationScheme,
isSegwitDerivationMode,
isUnsplitDerivationMode,
} from '@ledgerhq/live-common/lib/derivation'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies'
import {
encodeAccountId,
getNewAccountPlaceholderName,
getAccountPlaceholderName,
getWalletName,
} from '@ledgerhq/live-common/lib/account'
import { SHOW_LEGACY_NEW_ACCOUNT, SYNC_TIMEOUT } from 'config/constants'
import type { AccountRaw, OperationRaw, OperationType } from '@ledgerhq/live-common/lib/types'
import type {
CryptoCurrency,
AccountRaw,
OperationRaw,
OperationType,
} from '@ledgerhq/live-common/lib/types'
import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc'
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'
export function isValidAddress(core: *, currency: *, address: string): boolean {
@ -33,112 +46,71 @@ type Props = {
isUnsubscribed: () => boolean,
}
const shouldShowNewAccount = (currency, derivationMode) =>
derivationMode === ''
? !!SHOW_LEGACY_NEW_ACCOUNT || !currency.supportsSegwit
: derivationMode === 'segwit'
export async function scanAccountsOnDevice(props: Props): Promise<AccountRaw[]> {
const { devicePath, currencyId, onAccountScanned, core, isUnsubscribed } = props
const currency = getCryptoCurrencyById(currencyId)
const commonParams = {
let allAccounts = []
const derivationModes = getDerivationModesForCurrency(currency)
for (let i = 0; i < derivationModes.length; i++) {
const derivationMode = derivationModes[i]
const accounts = await scanAccountsOnDeviceBySegwit({
core,
currencyId,
currency,
onAccountScanned,
devicePath,
isUnsubscribed,
}
let allAccounts = []
const nonSegwitAccounts = await scanAccountsOnDeviceBySegwit({
...commonParams,
showNewAccount: !!SHOW_LEGACY_NEW_ACCOUNT || !currency.supportsSegwit,
isSegwit: false,
isUnsplit: false,
showNewAccount: shouldShowNewAccount(currency, derivationMode),
derivationMode,
})
allAccounts = allAccounts.concat(nonSegwitAccounts)
if (currency.supportsSegwit) {
const segwitAccounts = await scanAccountsOnDeviceBySegwit({
...commonParams,
showNewAccount: true,
isSegwit: true,
isUnsplit: false,
})
allAccounts = allAccounts.concat(segwitAccounts)
}
// TODO: put that info inside currency itself
if (currencyId in splittedCurrencies) {
const splittedAccounts = await scanAccountsOnDeviceBySegwit({
...commonParams,
isSegwit: false,
showNewAccount: false,
isUnsplit: true,
})
allAccounts = allAccounts.concat(splittedAccounts)
if (currency.supportsSegwit) {
const segwitAccounts = await scanAccountsOnDeviceBySegwit({
...commonParams,
showNewAccount: false,
isUnsplit: true,
isSegwit: true,
})
allAccounts = allAccounts.concat(segwitAccounts)
}
allAccounts = allAccounts.concat(accounts)
}
return allAccounts
}
function encodeWalletName({
publicKey,
currencyId,
isSegwit,
isUnsplit,
}: {
publicKey: string,
currencyId: string,
isSegwit: boolean,
isUnsplit: boolean,
}) {
const splitConfig = isUnsplit ? splittedCurrencies[currencyId] || null : null
return `${publicKey}__${currencyId}${isSegwit ? '_segwit' : ''}${splitConfig ? '_unsplit' : ''}`
}
async function scanAccountsOnDeviceBySegwit({
core,
devicePath,
currencyId,
currency,
onAccountScanned,
isUnsubscribed,
isSegwit,
isUnsplit,
derivationMode,
showNewAccount,
}: {
core: *,
devicePath: string,
currencyId: string,
currency: CryptoCurrency,
onAccountScanned: AccountRaw => void,
isUnsubscribed: () => boolean,
isSegwit: boolean, // FIXME all segwit to change to 'purpose'
derivationMode: string,
showNewAccount: boolean,
isUnsplit: boolean,
}): Promise<AccountRaw[]> {
const customOpts =
isUnsplit && splittedCurrencies[currencyId] ? splittedCurrencies[currencyId] : null
const { coinType } = customOpts ? customOpts.coinType : getCryptoCurrencyById(currencyId)
const isSegwit = isSegwitDerivationMode(derivationMode)
const unsplitFork = isUnsplitDerivationMode(derivationMode) ? currency.forkedFrom : null
const { coinType } = unsplitFork ? getCryptoCurrencyById(unsplitFork) : currency
const path = `${isSegwit ? '49' : '44'}'/${coinType}'`
const { publicKey } = await withDevice(devicePath)(async transport =>
const { publicKey: seedIdentifier } = await withDevice(devicePath)(async transport =>
new Btc(transport).getWalletPublicKey(path, false, isSegwit),
)
if (isUnsubscribed()) return []
const walletName = encodeWalletName({ publicKey, currencyId, isSegwit, isUnsplit })
const walletName = getWalletName({
seedIdentifier,
currency,
derivationMode,
})
// retrieve or create the wallet
const wallet = await getOrCreateWallet(core, walletName, { currencyId, isSegwit, isUnsplit })
const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
const accountsCount = await wallet.getAccountCount()
// recursively scan all accounts on device on the given app
@ -148,13 +120,13 @@ async function scanAccountsOnDeviceBySegwit({
wallet,
walletName,
devicePath,
currencyId,
currency,
accountsCount,
accountIndex: 0,
accounts: [],
onAccountScanned,
isSegwit,
isUnsplit,
seedIdentifier,
derivationMode,
showNewAccount,
isUnsubscribed,
})
@ -227,13 +199,13 @@ async function scanNextAccount(props: {
walletName: string,
core: *,
devicePath: string,
currencyId: string,
currency: CryptoCurrency,
seedIdentifier: string,
derivationMode: string,
accountsCount: number,
accountIndex: number,
accounts: AccountRaw[],
onAccountScanned: AccountRaw => void,
isSegwit: boolean,
isUnsplit: boolean,
showNewAccount: boolean,
isUnsubscribed: () => boolean,
}): Promise<AccountRaw[]> {
@ -242,13 +214,13 @@ async function scanNextAccount(props: {
wallet,
walletName,
devicePath,
currencyId,
currency,
accountsCount,
accountIndex,
accounts,
onAccountScanned,
isSegwit,
isUnsplit,
derivationMode,
seedIdentifier,
showNewAccount,
isUnsubscribed,
} = props
@ -275,12 +247,12 @@ async function scanNextAccount(props: {
const account = await buildAccountRaw({
njsAccount,
isSegwit,
isUnsplit,
seedIdentifier,
derivationMode,
accountIndex,
wallet,
walletName,
currencyId,
currency,
core,
ops,
})
@ -316,13 +288,11 @@ export async function getOrCreateWallet(
core: *,
walletName: string,
{
currencyId,
isSegwit,
isUnsplit,
currency,
derivationMode,
}: {
currencyId: string,
isSegwit: boolean,
isUnsplit: boolean,
currency: CryptoCurrency,
derivationMode: string,
},
): NJSWallet {
const pool = core.getPoolInstance()
@ -330,24 +300,21 @@ export async function getOrCreateWallet(
const wallet = await timeoutTagged('getWallet', 5000, pool.getWallet(walletName))
return wallet
} catch (err) {
const currency = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currencyId))
const splitConfig = isUnsplit ? splittedCurrencies[currencyId] || null : null
const coinType = splitConfig ? splitConfig.coinType : '<coin_type>'
const walletConfig = isSegwit
const currencyCore = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currency.id))
const derivationScheme = getDerivationScheme({ currency, derivationMode })
const walletConfig = isSegwitDerivationMode(derivationMode)
? {
KEYCHAIN_ENGINE: 'BIP49_P2SH',
KEYCHAIN_DERIVATION_SCHEME: `49'/${coinType}'/<account>'/<node>/<address>`,
KEYCHAIN_DERIVATION_SCHEME: derivationScheme,
}
: splitConfig
? {
KEYCHAIN_DERIVATION_SCHEME: `44'/${coinType}'/<account>'/<node>/<address>`,
: {
KEYCHAIN_DERIVATION_SCHEME: derivationScheme,
}
: undefined
const njsWalletConfig = createWalletConfig(core, walletConfig)
const wallet = await timeoutTagged(
'createWallet',
10000,
core.getPoolInstance().createWallet(walletName, currency, njsWalletConfig),
core.getPoolInstance().createWallet(walletName, currencyCore, njsWalletConfig),
)
return wallet
}
@ -355,21 +322,20 @@ export async function getOrCreateWallet(
async function buildAccountRaw({
njsAccount,
isSegwit,
isUnsplit,
seedIdentifier,
derivationMode,
wallet,
walletName,
currencyId,
currency,
core,
accountIndex,
ops,
}: {
njsAccount: NJSAccount,
isSegwit: boolean,
isUnsplit: boolean,
wallet: NJSWallet,
seedIdentifier: string,
walletName: string,
currencyId: string,
currency: CryptoCurrency,
derivationMode: string,
accountIndex: number,
core: *,
ops: NJSOperation[],
@ -377,7 +343,6 @@ async function buildAccountRaw({
const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance())
const balance = njsBalance.toLong()
const jsCurrency = getCryptoCurrencyById(currencyId)
const { derivations } = await timeoutTagged(
'getAccountCreationInfo',
10000,
@ -416,29 +381,29 @@ async function buildAccountRaw({
ops.sort((a, b) => b.getDate() - a.getDate())
const operations = ops.map(op => buildOperationRaw({ core, op, xpub }))
const currency = getCryptoCurrencyById(currencyId)
const name =
operations.length === 0
? getNewAccountPlaceholderName(currency, accountIndex)
: getAccountPlaceholderName(
? getNewAccountPlaceholderName({ currency, index: accountIndex, derivationMode })
: getAccountPlaceholderName({
currency,
accountIndex,
(currency.supportsSegwit && !isSegwit) || false,
isUnsplit,
)
index: accountIndex,
derivationMode,
})
const rawAccount: AccountRaw = {
id: accountIdHelper.encode({
id: encodeAccountId({
type: 'libcore',
version: '1',
xpub,
walletName,
currencyId: currency.id,
xpubOrAddress: xpub,
derivationMode,
}),
seedIdentifier,
derivationMode,
xpub,
path: walletPath,
name,
isSegwit,
freshAddress,
freshAddressPath,
balance,
@ -447,8 +412,8 @@ async function buildAccountRaw({
index: accountIndex,
operations,
pendingOperations: [],
currencyId,
unitMagnitude: jsCurrency.units[0].magnitude,
currencyId: currency.id,
unitMagnitude: currency.units[0].magnitude,
lastSyncDate: new Date().toISOString(),
}
@ -501,23 +466,26 @@ function buildOperationRaw({
}
export async function syncAccount({
accountId,
freshAddressPath,
currencyId,
index,
core,
xpub,
derivationMode,
seedIdentifier,
currency,
index,
}: {
core: *,
accountId: string,
freshAddressPath: string,
currencyId: string,
xpub: string,
derivationMode: string,
seedIdentifier: string,
currency: CryptoCurrency,
index: number,
}) {
const decodedAccountId = accountIdHelper.decode(accountId)
const { walletName } = decodedAccountId
const isSegwit = isSegwitPath(freshAddressPath)
const isUnsplit = isUnsplitPath(freshAddressPath, splittedCurrencies[currencyId])
const njsWallet = await getOrCreateWallet(core, walletName, { currencyId, isSegwit, isUnsplit })
const walletName = getWalletName({
seedIdentifier,
derivationMode,
currency,
})
const njsWallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
let njsAccount
let requiresCacheFlush = false
@ -531,7 +499,7 @@ export async function syncAccount({
10000,
njsWallet.getExtendedKeyAccountCreationInfo(index),
)
extendedInfos.extendedKeys.push(decodedAccountId.xpub)
extendedInfos.extendedKeys.push(xpub)
njsAccount = await timeoutTagged(
'newAWEKI',
10000,
@ -548,12 +516,12 @@ export async function syncAccount({
const syncedRawAccount = await buildAccountRaw({
njsAccount,
isSegwit,
isUnsplit,
derivationMode,
seedIdentifier,
accountIndex: index,
wallet: njsWallet,
walletName,
currencyId,
currency,
core,
ops,
})
@ -577,29 +545,30 @@ export async function scanAccountsFromXPUB({
core,
currencyId,
xpub,
isSegwit,
isUnsplit,
derivationMode,
seedIdentifier,
}: {
core: *,
currencyId: string,
xpub: string,
isSegwit: boolean,
isUnsplit: boolean,
derivationMode: string,
seedIdentifier: string,
}) {
const currency = getCryptoCurrencyById(currencyId)
const walletName = encodeWalletName({
publicKey: `debug_${xpub}`,
currencyId,
isSegwit,
isUnsplit,
const walletName = getWalletName({
currency,
seedIdentifier: 'debug',
derivationMode,
})
const wallet = await getOrCreateWallet(core, walletName, { currencyId, isSegwit, isUnsplit })
const wallet = await getOrCreateWallet(core, walletName, { currency, derivationMode })
await wallet.eraseDataSince(new Date(0))
const index = 0
const isSegwit = isSegwitDerivationMode(derivationMode)
const extendedInfos = {
index,
owners: ['main'],
@ -616,12 +585,12 @@ export async function scanAccountsFromXPUB({
const ops = await query.complete().execute()
const rawAccount = await buildAccountRaw({
njsAccount: account,
isSegwit,
isUnsplit,
derivationMode,
seedIdentifier,
accountIndex: index,
wallet,
walletName,
currencyId,
currency,
core,
ops,
})

4
src/reducers/accounts.js

@ -2,7 +2,7 @@
import { createSelector } from 'reselect'
import { handleActions } from 'redux-actions'
import { createAccountModel } from '@ledgerhq/live-common/lib/models/account'
import accountModel from 'helpers/accountModel'
import logger from 'logger'
import type { Account, AccountRaw } from '@ledgerhq/live-common/lib/types'
import { OUTDATED_CONSIDERED_DELAY, DEBUG_SYNC } from 'config/constants'
@ -10,8 +10,6 @@ import { OUTDATED_CONSIDERED_DELAY, DEBUG_SYNC } from 'config/constants'
export type AccountsState = Account[]
const state: AccountsState = []
const accountModel = createAccountModel()
const handlers: Object = {
SET_ACCOUNTS: (
state: AccountsState,

2
src/reducers/settings.js

@ -5,7 +5,7 @@ import {
findCurrencyByTicker,
getCryptoCurrencyById,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
} from '@ledgerhq/live-common/lib/currencies'
import { listCryptoCurrencies } from 'config/cryptocurrencies'
import languages from 'config/languages'
import { createSelector } from 'reselect'

134
yarn.lock

@ -1487,6 +1487,13 @@
core-js "^2.5.6"
regenerator-runtime "^0.11.1"
"@babel/runtime@^7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
dependencies:
regenerator-runtime "^0.12.0"
"@babel/template@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
@ -1738,10 +1745,10 @@
bindings "^1.3.0"
nan "^2.6.2"
"@ledgerhq/live-common@3.8.0":
version "3.8.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.8.0.tgz#8a956f4995c6006d7788a98959d0b0cab92307f6"
integrity sha512-ZfhNP9Zt6LGVJhwIdGqeqUZdele6cWEiGDsuXl5NGxS5BGuFZFp7MfrQ7IQWIaePQ5/A1FMy4SoOkgdPskqtvw==
"@ledgerhq/live-common@4.0.0-beta.1":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-4.0.0-beta.1.tgz#52ed90757761a08a5f5c9c40c1fed0cb2f1fb4ca"
integrity sha512-Ms06Za/EI8yP/4GOAziCMecqIssZbkS5tjSCgaqd1h0zHLzM6i6PvgjhJyvG5SrET6+uQVp4YM4qbo2tc6irjQ==
dependencies:
axios "^0.18.0"
bignumber.js "^7.2.1"
@ -1751,6 +1758,7 @@
numeral "^2.0.6"
prando "^3.0.1"
react "^16.4.0"
react-i18next "^8.0.7"
react-redux "^5.0.7"
redux "^4.0.0"
reselect "^3.0.1"
@ -1783,6 +1791,21 @@
node-fetch "^2.1.1"
url-template "^2.0.8"
"@octokit/rest@^15.2.6":
version "15.13.1"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-15.13.1.tgz#68b12ff0e470ad70c90e99bbb69f16bcdf62edb4"
integrity sha512-r6aRAZaaUZkTqtI4seaSamvgqmYswXpxclIqUrwtFtOuRAnE7l0aeWU252vQ/mxd1wKZWMq1oFChzk0/qzcYcg==
dependencies:
before-after-hook "^1.1.0"
btoa-lite "^1.0.0"
debug "^3.1.0"
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.0"
lodash "^4.17.4"
node-fetch "^2.1.1"
universal-user-agent "^2.0.0"
url-template "^2.0.8"
"@posthtml/esm@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@posthtml/esm/-/esm-1.0.0.tgz#09bcb28a02438dcee22ad1970ca1d85a000ae0cf"
@ -5490,6 +5513,14 @@ create-react-class@^15.5.2, create-react-class@^15.6.2:
loose-envify "^1.3.1"
object-assign "^4.1.1"
create-react-context@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==
dependencies:
fbjs "^0.8.0"
gud "^1.0.0"
crocket@^0.9.11:
version "0.9.11"
resolved "https://registry.yarnpkg.com/crocket/-/crocket-0.9.11.tgz#288fca11ef0d3dd239b62c488265f30c8edfb0c5"
@ -7679,7 +7710,7 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
fbjs@^0.8.12, fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9:
fbjs@^0.8.0, fbjs@^0.8.12, fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
@ -7889,6 +7920,27 @@ flow-typed@^2.4.0:
which "^1.3.0"
yargs "^4.2.0"
flow-typed@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/flow-typed/-/flow-typed-2.5.1.tgz#0ff565cc94d2af8c557744ba364b6f14726a6b9f"
integrity sha1-D/VlzJTSr4xVd0S6NktvFHJqa58=
dependencies:
"@octokit/rest" "^15.2.6"
babel-polyfill "^6.26.0"
colors "^1.1.2"
fs-extra "^5.0.0"
glob "^7.1.2"
got "^7.1.0"
md5 "^2.1.0"
mkdirp "^0.5.1"
rimraf "^2.6.2"
semver "^5.5.0"
table "^4.0.2"
through "^2.3.8"
unzipper "^0.8.11"
which "^1.3.0"
yargs "^4.2.0"
flush-write-stream@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
@ -8421,6 +8473,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
gud@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
gzip-size@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520"
@ -8647,6 +8704,13 @@ hoist-non-react-statics@1.x.x, hoist-non-react-statics@^1.2.0:
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
integrity sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=
hoist-non-react-statics@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz#fba3e7df0210eb9447757ca1a7cb607162f0a364"
integrity sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ==
dependencies:
react-is "^16.3.2"
hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
@ -10709,6 +10773,11 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.3:
pseudomap "^1.0.2"
yallist "^2.1.2"
macos-release@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-1.1.0.tgz#831945e29365b470aa8724b0ab36c8f8959d10fb"
integrity sha512-mmLbumEYMi5nXReB9js3WGsB8UE6cDBWyIO62Z4DNx6GbRhDxHNjA1MlzSpJ2S2KM1wyiPRA0d19uHWYYvMHjA==
make-dir@^1.0.0, make-dir@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@ -11760,6 +11829,14 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
os-name@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/os-name/-/os-name-2.0.1.tgz#b9a386361c17ae3a21736ef0599405c9a8c5dc5e"
integrity sha1-uaOGNhwXrjohc27wWZQFyajF3F4=
dependencies:
macos-release "^1.0.0"
win-release "^1.0.0"
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@ -12784,6 +12861,14 @@ qrcode@^1.2.0:
pngjs "^3.3.0"
yargs "^8.0.2"
qrloop@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/qrloop/-/qrloop-0.6.1.tgz#167f8868f03018c7625b0e887049830e9253beae"
integrity sha512-1wJwoKpukmkfqwzyb9lMMEE9BDWXlKv63J/gdWMIeUfzgSR3QrZ3VKTtjN9Xvyo1DWwJd/G3FuMtJaH9cf+XUw==
dependencies:
flow-typed "^2.5.1"
md5 "^2.2.1"
qs@6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
@ -13024,6 +13109,16 @@ react-i18next@^7.7.0:
html-parse-stringify2 "2.0.1"
prop-types "^15.6.0"
react-i18next@^8.0.7:
version "8.0.7"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-8.0.7.tgz#9e6ffdaa01f4454ba216fa50377a7c2ba6f1e662"
integrity sha512-oJDVe5X8QK72NYYH7/VQtopONZPcNiarlWdVuCSkn9PucQCEAOf/yRt0V2LdjC4nBY48Y20/540glwR1nlE9gA==
dependencies:
"@babel/runtime" "^7.1.2"
create-react-context "0.2.3"
hoist-non-react-statics "3.0.1"
html-parse-stringify2 "2.0.1"
react-icon-base@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.0.tgz#a196e33fdf1e7aaa1fda3aefbb68bdad9e82a79d"
@ -13056,6 +13151,11 @@ react-is@^16.3.1, react-is@^16.4.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
integrity sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==
react-is@^16.3.2:
version "16.5.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3"
integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==
react-key-handler@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/react-key-handler/-/react-key-handler-1.0.1.tgz#1fc0f4f4855f506a192c2cbe9fe8cb78fc553191"
@ -13540,6 +13640,11 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-transform@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@ -14217,6 +14322,11 @@ semver-diff@^2.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.0.1:
version "5.5.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@ -15694,6 +15804,13 @@ unist-util-visit@^1.1.0, unist-util-visit@^1.3.0:
dependencies:
unist-util-is "^2.1.1"
universal-user-agent@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.0.1.tgz#18e591ca52b1cb804f6b9cbc4c336cf8191f80e1"
integrity sha512-vz+heWVydO0iyYAa65VHD7WZkYzhl7BeNVy4i54p4TF8OMiLSXdbuQe4hm+fmWAsL+rVibaQHXfhvkw3c1Ws2w==
dependencies:
os-name "^2.0.1"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@ -16455,6 +16572,13 @@ wif@^2.0.1:
dependencies:
bs58check "<3.0.0"
win-release@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/win-release/-/win-release-1.1.1.tgz#5fa55e02be7ca934edfc12665632e849b72e5209"
integrity sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=
dependencies:
semver "^5.0.1"
window-or-global@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/window-or-global/-/window-or-global-1.0.1.tgz#dbe45ba2a291aabc56d62cf66c45b7fa322946de"

Loading…
Cancel
Save