Browse Source

Merge pull request #1475 from LedgerHQ/develop

Prepare for patch release 1.1.8
master
Gaëtan Renaudeau 6 years ago
committed by GitHub
parent
commit
9e536b644e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .circleci/config.yml
  2. 1
      .eslintrc
  3. 8
      .gitignore
  4. 12
      README.md
  5. 4
      build/linux/arch/PKGBUILD
  6. 13
      package.json
  7. 85
      scripts/create-draft-release.js
  8. 8
      scripts/helpers/display-env.sh
  9. 14
      scripts/release.sh
  10. 4
      src/api/Ethereum.js
  11. 4
      src/api/Fees.js
  12. 6
      src/api/network.js
  13. 5
      src/bridge/EthereumJSBridge.js
  14. 3
      src/bridge/LibcoreBridge.js
  15. 4
      src/bridge/RippleJSBridge.js
  16. 1
      src/bridge/index.js
  17. 3
      src/commands/getCurrentFirmware.js
  18. 6
      src/commands/getDeviceInfo.js
  19. 2
      src/commands/getIsGenuine.js
  20. 4
      src/commands/getLatestFirmwareForDevice.js
  21. 2
      src/commands/index.js
  22. 6
      src/commands/installApp.js
  23. 2
      src/commands/installMcu.js
  24. 2
      src/commands/installOsuFirmware.js
  25. 4
      src/commands/libcoreGetFees.js
  26. 21
      src/commands/libcoreHardReset.js
  27. 4
      src/commands/listAppVersions.js
  28. 5
      src/commands/listApps.js
  29. 5
      src/commands/listCategories.js
  30. 3
      src/commands/shouldFlashMcu.js
  31. 10
      src/commands/uninstallApp.js
  32. 16
      src/components/DashboardPage/EmptyState.js
  33. 3
      src/components/EnsureDeviceApp.js
  34. 2
      src/components/GenuineCheck.js
  35. 6
      src/components/IsUnlocked.js
  36. 6
      src/components/ManagerPage/AppSearchBar.js
  37. 22
      src/components/ManagerPage/AppsList.js
  38. 2
      src/components/ManagerPage/Dashboard.js
  39. 5
      src/components/ManagerPage/FirmwareUpdate.js
  40. 2
      src/components/ManagerPage/index.js
  41. 8
      src/components/Onboarding/OnboardingFooter.js
  42. 2
      src/components/Onboarding/steps/Analytics.js
  43. 4
      src/components/Onboarding/steps/Finish.js
  44. 34
      src/components/QRCodeExporter.js
  45. 3
      src/components/RecipientAddress/index.js
  46. 2
      src/components/RenderError.js
  47. 20
      src/components/SettingsPage/CleanButton.js
  48. 2
      src/components/SettingsPage/DevModeButton.js
  49. 4
      src/components/SettingsPage/DisablePasswordModal.js
  50. 4
      src/components/SettingsPage/PasswordForm.js
  51. 4
      src/components/SettingsPage/PasswordModal.js
  52. 2
      src/components/SettingsPage/ResetButton.js
  53. 2
      src/components/SettingsPage/index.js
  54. 2
      src/components/TopBar/index.js
  55. 5
      src/components/base/InputCurrency/index.js
  56. 1
      src/components/base/QRCode/index.js
  57. 6
      src/components/base/Select/createStyles.js
  58. 6
      src/components/base/Select/index.js
  59. 5
      src/components/modals/AccountSettingRenderBody.js
  60. 4
      src/components/modals/Disclaimer.js
  61. 4
      src/components/modals/UpdateFirmware/index.js
  62. 36
      src/config/errors.js
  63. 26
      src/helpers/apps/installApp.js
  64. 15
      src/helpers/apps/listAppVersions.js
  65. 3
      src/helpers/apps/listApps.js
  66. 5
      src/helpers/apps/listCategories.js
  67. 18
      src/helpers/apps/uninstallApp.js
  68. 3
      src/helpers/debugAppInfosForCurrency/btc.js
  69. 5
      src/helpers/devices/getCurrentFirmware.js
  70. 12
      src/helpers/devices/getDeviceInfo.js
  71. 6
      src/helpers/devices/getDeviceVersion.js
  72. 12
      src/helpers/devices/getIsGenuine.js
  73. 36
      src/helpers/devices/getLatestFirmwareForDevice.js
  74. 2
      src/helpers/devices/shouldFlashMcu.js
  75. 3
      src/helpers/firmware/getFirmwareInfo.js
  76. 7
      src/helpers/firmware/getNextMCU.js
  77. 15
      src/helpers/firmware/installFinalFirmware.js
  78. 11
      src/helpers/firmware/installMcu.js
  79. 12
      src/helpers/firmware/installOsuFirmware.js
  80. 4
      src/helpers/getAddressForCurrency/btc.js
  81. 13
      src/helpers/hardReset.js
  82. 9
      src/helpers/libcore.js
  83. 35
      src/helpers/reset.js
  84. 14
      src/helpers/socket.js
  85. 132
      src/helpers/types.js
  86. 4
      static/i18n/en/errors.json
  87. 64
      test-e2e/nav_to_settings.spec.js
  88. 306
      yarn.lock

4
.circleci/config.yml

@ -13,10 +13,10 @@ jobs:
- checkout
- restore_cache:
keys:
- v7-yarn-packages-{{ checksum "yarn.lock" }}
- v8-yarn-packages-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
key: v7-yarn-packages-{{ checksum "yarn.lock" }}
key: v8-yarn-packages-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run: yarn lint

1
.eslintrc

@ -21,6 +21,7 @@
"jest": false,
"describe": false,
"beforeEach": false,
"afterEach": false,
"test": false,
"it": false,
"expect": false,

8
.gitignore

@ -10,11 +10,3 @@
/build/linux/arch/src
/build/linux/arch/*.tar.gz
/build/linux/arch/*.tar.xz
# TODO this should be in devs global gitignore
# it makes no sense to have it here
*.log
.DS_Store
.vscode
thumbs.db
jsconfig.json

12
README.md

@ -159,3 +159,15 @@ yarn reset-files
├── webpack : build configuration
└── yarn.lock
```
## Troubleshooting
#### The 'gyp==0.1' distribution was not found and is required by the application
You will need to install the python gyp module
```
pip install git+https://chromium.googlesource.com/external/gyp
```
see [stackoverflow/40025591](https://stackoverflow.com/questions/40025591/the-gyp-0-1-distribution-was-not-found)

4
build/linux/arch/PKGBUILD

@ -2,7 +2,7 @@
# shellcheck disable=SC2154,SC2034,SC2164
pkgname=ledger-live
pkgver=1.0.7
pkgver=1.1.0
pkgrel=1
pkgdesc="Open source companion app for your Ledger devices"
arch=('x86_64')
@ -15,7 +15,7 @@ changelog=
source=("https://github.com/LedgerHQ/ledger-live-desktop/archive/v${pkgver}.tar.gz"
"ledger-live.desktop")
md5sums=('d60d772a03c0a1c59df07f93b0268a4c'
md5sums=('5dc6bd1e8d6dedcfe760d1c75565aad8'
'52705147909a0a988907a23a71199092')
# TODO sign with ledger pgp
validpgpkeys=()

13
package.json

@ -13,10 +13,11 @@
"dist": "bash ./scripts/dist.sh",
"dist:dir": "bash ./scripts/dist-dir.sh",
"compile": "bash ./scripts/compile.sh",
"lint": "eslint src webpack .storybook",
"lint": "eslint src webpack .storybook test-e2e",
"flow": "flow",
"test": "jest",
"prettier": "prettier --write \"{src,webpack,.storybook}/**/*.{js,json}\"",
"test": "jest src",
"test-e2e": "jest test-e2e",
"prettier": "prettier --write \"{src,webpack,.storybook,test-e2e}/**/*.{js,json}\"",
"ci": "yarn lint && yarn flow && yarn prettier && yarn test",
"storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444",
"publish-storybook": "bash ./scripts/legacy/publish-storybook.sh",
@ -38,7 +39,7 @@
"@ledgerhq/hw-transport": "^4.13.0",
"@ledgerhq/hw-transport-node-hid": "4.22.0",
"@ledgerhq/ledger-core": "2.0.0-rc.6",
"@ledgerhq/live-common": "3.0.2",
"@ledgerhq/live-common": "^3.3.0",
"animated": "^0.2.2",
"async": "^2.6.1",
"axios": "^0.18.0",
@ -118,6 +119,7 @@
"@babel/preset-flow": "7.0.0-beta.42",
"@babel/preset-react": "7.0.0-beta.42",
"@babel/preset-stage-0": "7.0.0-beta.42",
"@octokit/rest": "^15.10.0",
"@storybook/addon-actions": "^3.4.7",
"@storybook/addon-knobs": "^3.4.7",
"@storybook/addon-links": "^3.4.7",
@ -134,7 +136,7 @@
"chance": "^1.0.13",
"concurrently": "3.5.1",
"dotenv": "^5.0.1",
"electron": "1.8.7",
"electron": "1.8.8",
"electron-builder": "20.14.7",
"electron-devtools-installer": "^2.2.3",
"electron-rebuild": "^1.7.3",
@ -158,6 +160,7 @@
"prettier": "^1.13.5",
"react-hot-loader": "^4.3.2",
"react-test-renderer": "^16.4.1",
"spectron": "^3.8.0",
"webpack": "^4.6.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-cli": "^2.0.14",

85
scripts/create-draft-release.js

@ -0,0 +1,85 @@
#!/usr/bin/env node
/* eslint-disable no-console */
const util = require('util')
const exec = util.promisify(require('child_process').exec)
const octokit = require('@octokit/rest')()
const repo = {
owner: 'LedgerHQ',
repo: 'ledger-live-desktop',
}
async function getTag() {
const { stdout } = await exec('git tag --points-at HEAD')
const tag = stdout.replace('\n', '')
if (!tag) {
throw new Error(`Unable to get current tag. Is your HEAD on a tagged commit?`)
}
return tag
}
async function checkDraft(tag) {
const { status, data } = await octokit.repos.getReleases(repo)
if (status !== 200) {
throw new Error(`Got HTTP status ${status} when trying to fetch releases list.`)
}
for (const release of data) {
if (release.tag_name === tag) {
if (release.draft) {
return true
}
throw new Error(`A release tagged ${tag} exists but is not a draft.`)
}
}
return false
}
async function createDraft(tag) {
const params = {
...repo,
tag_name: tag,
name: tag,
draft: true,
prerelease: true,
}
const { status } = await octokit.repos.createRelease(params)
if (status !== 201) {
throw new Error(`Got HTTP status ${status} when trying to create the release draft.`)
}
}
async function main() {
try {
const token = process.env.GH_TOKEN
const tag = await getTag()
octokit.authenticate({
type: 'token',
token,
})
const existingDraft = await checkDraft(tag)
if (!existingDraft) {
console.log(`No draft exists for ${tag}, creating...`)
createDraft(tag)
} else {
console.log(`A draft already exists for ${tag}, nothing to do.`)
}
} catch (e) {
console.error(e)
process.exit(1)
}
}
main()

8
scripts/helpers/display-env.sh

@ -7,9 +7,15 @@ if [ "$GIT_REVISION" == "" ]; then
GIT_REVISION=$(git rev-parse HEAD)
fi
if [[ $(uname) == 'Darwin' ]]; then
osVersion="$(sw_vers -productName) $(sw_vers -productVersion)"
else
osVersion="$(uname -srmo)"
fi
echo
printf " │ \\e[4;1m%s\\e[0;0m\\n" "Ledger Live Desktop - ${GIT_REVISION}"
printf " │ \\e[1;30m%s\\e[1;0m\\n" "$(uname -srmo)"
printf " │ \\e[1;30m%s\\e[1;0m\\n" "${osVersion}"
printf " │ \\e[2;1mcommit \\e[0;33m%s\\e[0;0m\\n" "$(git rev-parse HEAD)"
echo

14
scripts/release.sh

@ -25,6 +25,11 @@ fi
if [ ! -d "static/fonts/museosans" ]; then
if ! command -v aws ; then
if ! command -v apt ; then
echo "Museo Sans is missing, and I can't fetch it (no aws, no apt)" >&2
exit 1
fi
runJob "sudo apt install awscli" "installing aws cli..." "installed aws cli" "failed to install aws cli"
fi
@ -52,6 +57,15 @@ fi
# exit 1
# fi
if [[ $(uname) == 'Linux' ]]; then # only run it on one target, to prevent race conditions
runJob \
"node scripts/create-draft-release.js" \
"creating a draft release on GitHub (if needed)..." \
"draft release ready" \
"failed to create a draft release"
fi
runJob "yarn compile" "compiling..." "compiled" "failed to compile" "verbose"
runJob \

4
src/api/Ethereum.js

@ -1,12 +1,10 @@
// @flow
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { BigNumber } from 'bignumber.js'
import { createCustomErrorClass } from 'helpers/errors'
import { LedgerAPINotAvailable } from 'config/errors'
import network from './network'
import { blockchainBaseURL } from './Ledger'
export const LedgerAPINotAvailable = createCustomErrorClass('LedgerAPINotAvailable')
export type Block = { height: number } // TODO more fields actually
export type Tx = {
hash: string,

4
src/api/Fees.js

@ -2,12 +2,10 @@
import invariant from 'invariant'
import LRU from 'lru-cache'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import { createCustomErrorClass } from 'helpers/errors'
import { FeeEstimationFailed } from 'config/errors'
import { blockchainBaseURL } from './Ledger'
import network from './network'
const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed')
export type Fees = {
[_: string]: number,
}

6
src/api/network.js

@ -3,13 +3,9 @@ import axios from 'axios'
import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants'
import { retry } from 'helpers/promise'
import logger from 'logger'
import { createCustomErrorClass } from 'helpers/errors'
import { LedgerAPIErrorWithMessage, LedgerAPIError, NetworkDown } from 'config/errors'
import anonymizer from 'helpers/anonymizer'
export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage')
export const LedgerAPIError = createCustomErrorClass('LedgerAPIError')
export const NetworkDown = createCustomErrorClass('NetworkDown')
const userFriendlyError = <A>(p: Promise<A>, { url, method, startTime }): Promise<A> =>
p.catch(error => {
let errorToThrow

5
src/bridge/EthereumJSBridge.js

@ -16,12 +16,9 @@ import { getDerivations } from 'helpers/derivations'
import getAddressCommand from 'commands/getAddress'
import signTransactionCommand from 'commands/signTransaction'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import { ETHAddressNonEIP } from 'config/errors'
import { NotEnoughBalance, ETHAddressNonEIP } from 'config/errors'
import type { EditProps, WalletBridge } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
type Transaction = {
recipient: string,
amount: BigNumber,

3
src/bridge/LibcoreBridge.js

@ -11,11 +11,10 @@ import libcoreSyncAccount from 'commands/libcoreSyncAccount'
import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast'
import libcoreGetFees from 'commands/libcoreGetFees'
import libcoreValidAddress from 'commands/libcoreValidAddress'
import { createCustomErrorClass } from 'helpers/errors'
import { NotEnoughBalance } from 'config/errors'
import type { WalletBridge, EditProps } from './types'
const NOT_ENOUGH_FUNDS = 52
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
const notImplemented = new Error('LibcoreBridge: not implemented')

4
src/bridge/RippleJSBridge.js

@ -20,11 +20,9 @@ import {
import FeesRippleKind from 'components/FeesField/RippleKind'
import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import { NotEnoughBalance } from 'config/errors'
import type { WalletBridge, EditProps } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
type Transaction = {
amount: BigNumber,
recipient: string,

1
src/bridge/index.js

@ -12,6 +12,7 @@ const perFamily = {
bitcoin: LibcoreBridge,
ripple: RippleJSBridge,
ethereum: EthereumJSBridge,
stellar: null,
}
if (USE_MOCK_DATA) {
const mockBridge = makeMockBridge()

3
src/commands/getCurrentFirmware.js

@ -4,6 +4,7 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware'
import type { FinalFirmware } from 'helpers/types'
type Input = {
deviceId: string | number,
@ -11,7 +12,7 @@ type Input = {
provider: number,
}
type Result = *
type Result = FinalFirmware
const cmd: Command<Input, Result> = createCommand('getCurrentFirmware', data =>
fromPromise(getCurrentFirmware(data)),

6
src/commands/getDeviceInfo.js

@ -5,13 +5,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
type Input = {
devicePath: string,
}
const cmd: Command<Input, DeviceInfo> = createCommand('getDeviceInfo', ({ devicePath }) =>
type Result = DeviceInfo
const cmd: Command<Input, Result> = createCommand('getDeviceInfo', ({ devicePath }) =>
fromPromise(withDevice(devicePath)(transport => getDeviceInfo(transport))),
)

2
src/commands/getIsGenuine.js

@ -2,7 +2,7 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import getIsGenuine from 'helpers/devices/getIsGenuine'
import { withDevice } from 'helpers/deviceAccess'

4
src/commands/getLatestFirmwareForDevice.js

@ -2,11 +2,11 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, OsuFirmware } from 'helpers/types'
import getLatestFirmwareForDevice from '../helpers/devices/getLatestFirmwareForDevice'
type Result = *
type Result = ?(OsuFirmware & { shouldFlashMcu: boolean })
const cmd: Command<DeviceInfo, Result> = createCommand('getLatestFirmwareForDevice', data =>
fromPromise(getLatestFirmwareForDevice(data)),

2
src/commands/index.js

@ -17,7 +17,6 @@ import installOsuFirmware from 'commands/installOsuFirmware'
import isDashboardOpen from 'commands/isDashboardOpen'
import libcoreGetFees from 'commands/libcoreGetFees'
import libcoreGetVersion from 'commands/libcoreGetVersion'
import libcoreHardReset from 'commands/libcoreHardReset'
import libcoreScanAccounts from 'commands/libcoreScanAccounts'
import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast'
import libcoreSyncAccount from 'commands/libcoreSyncAccount'
@ -49,7 +48,6 @@ const all: Array<Command<any, any>> = [
isDashboardOpen,
libcoreGetFees,
libcoreGetVersion,
libcoreHardReset,
libcoreScanAccounts,
libcoreSignAndBroadcast,
libcoreSyncAccount,

6
src/commands/installApp.js

@ -6,15 +6,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'
import installApp from 'helpers/apps/installApp'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
type Input = {
app: LedgerScriptParams,
app: ApplicationVersion,
devicePath: string,
targetId: string | number,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand(
'installApp',

2
src/commands/installMcu.js

@ -10,7 +10,7 @@ type Input = {
devicePath: string,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand('installMcu', ({ devicePath }) =>
fromPromise(withDevice(devicePath)(transport => installMcu(transport))),

2
src/commands/installOsuFirmware.js

@ -14,7 +14,7 @@ type Input = {
firmware: Firmware,
}
type Result = *
type Result = { success: boolean }
const cmd: Command<Input, Result> = createCommand(
'installOsuFirmware',

4
src/commands/libcoreGetFees.js

@ -6,9 +6,7 @@ import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc'
import * as accountIdHelper from 'helpers/accountId'
import { isValidAddress, libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore'
import { createCustomErrorClass } from 'helpers/errors'
const InvalidAddress = createCustomErrorClass('InvalidAddress')
import { InvalidAddress } from 'config/errors'
type BitcoinLikeTransaction = {
// TODO we rename this Transaction concept into transactionInput

21
src/commands/libcoreHardReset.js

@ -1,21 +0,0 @@
// @flow
import { createCommand } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import withLibcore from 'helpers/withLibcore'
import { createCustomErrorClass } from 'helpers/errors'
const HardResetFail = createCustomErrorClass('HardResetFail')
const cmd = createCommand('libcoreHardReset', () =>
fromPromise(
withLibcore(async core => {
const result = await core.getPoolInstance().eraseDataSince(new Date(0))
if (result !== core.ERROR_CODE.FUTURE_WAS_SUCCESSFULL) {
throw new HardResetFail(`Hard reset fail with ${result} (check core.ERROR_CODE)`)
}
}),
),
)
export default cmd

4
src/commands/listAppVersions.js

@ -2,11 +2,11 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, ApplicationVersion } from 'helpers/types'
import listAppVersions from 'helpers/apps/listAppVersions'
type Result = *
type Result = Array<ApplicationVersion>
const cmd: Command<DeviceInfo, Result> = createCommand('listAppVersions', deviceInfo =>
fromPromise(listAppVersions(deviceInfo)),

5
src/commands/listApps.js

@ -4,10 +4,11 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import listApps from 'helpers/apps/listApps'
import type { Application } from 'helpers/types'
type Input = {}
type Input = void
type Result = *
type Result = Array<Application>
const cmd: Command<Input, Result> = createCommand('listApps', () => fromPromise(listApps()))

5
src/commands/listCategories.js

@ -4,10 +4,11 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import listCategories from 'helpers/apps/listCategories'
import type { Category } from 'helpers/types'
type Input = {}
type Input = void
type Result = *
type Result = Array<Category>
const cmd: Command<Input, Result> = createCommand('listCategories', () =>
fromPromise(listCategories()),

3
src/commands/shouldFlashMcu.js

@ -2,9 +2,10 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import shouldFlashMcu from 'helpers/devices/shouldFlashMcu'
import type { DeviceInfo } from 'helpers/types'
type Result = boolean
const cmd: Command<DeviceInfo, Result> = createCommand('shouldFlashMcu', data =>

10
src/commands/uninstallApp.js

@ -6,20 +6,20 @@ import { withDevice } from 'helpers/deviceAccess'
import uninstallApp from 'helpers/apps/uninstallApp'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
type Input = {
app: LedgerScriptParams,
app: ApplicationVersion,
devicePath: string,
targetId: string | number,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand(
'uninstallApp',
({ devicePath, targetId, ...rest }) =>
fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, rest))),
({ devicePath, targetId, ...app }) =>
fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, app))),
)
export default cmd

16
src/components/DashboardPage/EmptyState.js

@ -45,15 +45,25 @@ class EmptyState extends PureComponent<Props, *> {
height="157"
/>
<Box mt={5} alignItems="center">
<Title>{t('app:emptyState.dashboard.title')}</Title>
<Title data-e2e="dashboard_empty_title">{t('app:emptyState.dashboard.title')}</Title>
<Description mt={3} style={{ maxWidth: 600 }}>
{t('app:emptyState.dashboard.desc')}
</Description>
<Box mt={5} horizontal style={{ width: 300 }} flow={3} justify="center">
<Button primary style={{ minWidth: 120 }} onClick={this.handleInstallApp}>
<Button
primary
style={{ minWidth: 120 }}
onClick={this.handleInstallApp}
data-e2e="dashboard_empty_OpenManager"
>
{t('app:emptyState.dashboard.buttons.installApp')}
</Button>
<Button outline style={{ minWidth: 120 }} onClick={() => openModal(MODAL_ADD_ACCOUNTS)}>
<Button
outline
style={{ minWidth: 120 }}
onClick={() => openModal(MODAL_ADD_ACCOUNTS)}
data-e2e="dashboard_empty_AddAccounts"
>
{t('app:emptyState.dashboard.buttons.addAccount')}
</Button>
</Box>

3
src/components/EnsureDeviceApp.js

@ -12,7 +12,6 @@ import getAddress from 'commands/getAddress'
import { createCancelablePolling } from 'helpers/promise'
import { standardDerivation } from 'helpers/derivations'
import { isSegwitPath } from 'helpers/bip32'
import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc'
import DeviceInteraction from 'components/DeviceInteraction'
import Text from 'components/base/Text'
@ -21,7 +20,7 @@ import IconUsb from 'icons/Usb'
import type { Device } from 'types/common'
import { WrongDeviceForAccount, CantOpenDevice } from 'config/errors'
import { WrongDeviceForAccount, CantOpenDevice, BtcUnmatchedApp } from 'config/errors'
import { getCurrentDevice } from 'reducers/devices'
const usbIcon = <IconUsb size={16} />

2
src/components/GenuineCheck.js

@ -9,7 +9,7 @@ import { delay, createCancelablePolling } from 'helpers/promise'
import logger from 'logger'
import type { T, Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import { GENUINE_TIMEOUT, DEVICE_INFOS_TIMEOUT, GENUINE_CACHE_DELAY } from 'config/constants'

6
src/components/IsUnlocked.js

@ -12,12 +12,12 @@ import { i } from 'helpers/staticPath'
import IconTriangleWarning from 'icons/TriangleWarning'
import db from 'helpers/db'
import hardReset from 'helpers/hardReset'
import { hardReset } from 'helpers/reset'
import { fetchAccounts } from 'actions/accounts'
import { isLocked, unlock } from 'reducers/application'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import Box from 'components/base/Box'
import InputPassword from 'components/base/InputPassword'
@ -26,8 +26,6 @@ import IconArrowRight from 'icons/ArrowRight'
import Button from './base/Button/index'
import ConfirmModal from './base/Modal/ConfirmModal'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type InputValue = {
password: string,
}

6
src/components/ManagerPage/AppSearchBar.js

@ -3,7 +3,7 @@
import React, { PureComponent, Fragment } from 'react'
import styled from 'styled-components'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
@ -23,8 +23,8 @@ const CrossContainer = styled(Box).attrs({
`
type Props = {
list: Array<LedgerScriptParams>,
children: (list: Array<LedgerScriptParams>) => React$Node,
list: Array<ApplicationVersion>,
children: (list: Array<ApplicationVersion>) => React$Node,
}
type State = {

22
src/components/ManagerPage/AppsList.js

@ -7,8 +7,7 @@ import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { compose } from 'redux'
import type { Device, T } from 'types/common'
import type { LedgerScriptParams } from 'helpers/types'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { Application, ApplicationVersion, DeviceInfo } from 'helpers/types'
import { developerModeSelector } from 'reducers/settings'
import listApps from 'commands/listApps'
@ -66,7 +65,7 @@ type Props = {
type State = {
status: Status,
error: ?Error,
filteredAppVersionsList: LedgerScriptParams[],
filteredAppVersionsList: Array<ApplicationVersion>,
appsLoaded: boolean,
app: string,
mode: Mode,
@ -102,9 +101,14 @@ class AppsList extends PureComponent<Props, State> {
filterAppVersions = (applicationsList, compatibleAppVersionsList) => {
if (!this.props.isDevMode) {
return compatibleAppVersionsList.filter(
version => applicationsList.find(e => e.id === version.app).category !== 2,
)
return compatibleAppVersionsList.filter(version => {
const app = applicationsList.find(e => e.id === version.app)
if (app) {
return app.category !== 2
}
return false
})
}
return compatibleAppVersionsList
}
@ -112,7 +116,7 @@ class AppsList extends PureComponent<Props, State> {
async fetchAppList() {
try {
const { deviceInfo } = this.props
const applicationsList = await listApps.send({}).toPromise()
const applicationsList: Array<Application> = await listApps.send().toPromise()
const compatibleAppVersionsList = await listAppVersions.send(deviceInfo).toPromise()
const filteredAppVersionsList = this.filterAppVersions(
applicationsList,
@ -131,7 +135,7 @@ class AppsList extends PureComponent<Props, State> {
}
}
handleInstallApp = (app: LedgerScriptParams) => async () => {
handleInstallApp = (app: ApplicationVersion) => async () => {
this.setState({ status: 'busy', app: app.name, mode: 'installing' })
try {
const {
@ -146,7 +150,7 @@ class AppsList extends PureComponent<Props, State> {
}
}
handleUninstallApp = (app: LedgerScriptParams) => async () => {
handleUninstallApp = (app: ApplicationVersion) => async () => {
this.setState({ status: 'busy', app: app.name, mode: 'uninstalling' })
try {
const {

2
src/components/ManagerPage/Dashboard.js

@ -4,7 +4,7 @@ import { translate } from 'react-i18next'
import styled from 'styled-components'
import type { T, Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import Box from 'components/base/Box'
import Text from 'components/base/Text'

5
src/components/ManagerPage/FirmwareUpdate.js

@ -9,7 +9,7 @@ import invariant from 'invariant'
import type { Device, T } from 'types/common'
import type { LedgerScriptParams } from 'helpers/types'
import type { DeviceInfo, OsuFirmware } from 'helpers/types'
import type { StepId } from 'components/modals/UpdateFirmware'
import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice'
@ -19,7 +19,6 @@ import installFinalFirmware from 'commands/installFinalFirmware'
import installMcu from 'commands/installMcu'
import DisclaimerModal from 'components/modals/UpdateFirmware/Disclaimer'
import UpdateModal from 'components/modals/UpdateFirmware'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import Tooltip from 'components/base/Tooltip'
import Box, { Card } from 'components/base/Box'
@ -43,7 +42,7 @@ type Props = {
}
type State = {
latestFirmware: ?LedgerScriptParams & ?{ shouldFlashMcu: boolean },
latestFirmware: ?OsuFirmware & ?{ shouldFlashMcu: boolean },
modal: ModalStatus,
stepId: ?StepId,
shouldFlash: boolean,

2
src/components/ManagerPage/index.js

@ -6,7 +6,7 @@ import { openURL } from 'helpers/linking'
import { urls } from 'config/urls'
import type { Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import Dashboard from './Dashboard'

8
src/components/Onboarding/OnboardingFooter.js

@ -26,7 +26,13 @@ const OnboardingFooter = ({
<Button outlineGrey onClick={() => prevStep()}>
{t('app:common.back')}
</Button>
<Button disabled={isContinueDisabled} primary onClick={() => nextStep()} ml="auto">
<Button
data-e2e="continue_button"
disabled={isContinueDisabled}
primary
onClick={() => nextStep()}
ml="auto"
>
{t('app:common.continue')}
</Button>
</OnboardingFooterWrapper>

2
src/components/Onboarding/steps/Analytics.js

@ -70,7 +70,7 @@ class Analytics extends PureComponent<StepProps, State> {
deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'}
/>
<StepContainerInner>
<Title>{t('onboarding:analytics.title')}</Title>
<Title data-e2e="onboarding_title">{t('onboarding:analytics.title')}</Title>
<Description>{t('onboarding:analytics.desc')}</Description>
<Box mt={5}>
<Container>

4
src/components/Onboarding/steps/Finish.js

@ -103,11 +103,11 @@ export default class Finish extends Component<StepProps, *> {
</Box>
<Box pt={5} align="center">
<Title>{t('onboarding:finish.title')}</Title>
<Title data-e2e="finish_title">{t('onboarding:finish.title')}</Title>
<Description>{t('onboarding:finish.desc')}</Description>
</Box>
<Box p={5}>
<Button primary onClick={() => finish()}>
<Button primary onClick={() => finish()} data-e2e="continue_button">
{t('onboarding:finish.openAppButton')}
</Button>
</Box>

34
src/components/QRCodeExporter.js

@ -1,31 +1,21 @@
// @flow
import React, { PureComponent } from 'react'
import { createSelector } from 'reselect'
import { connect } from 'react-redux'
import type { State } from 'reducers'
import { accountsSelector } from 'reducers/accounts'
import { makeChunks } from '@ledgerhq/live-common/lib/bridgestream/exporter'
import QRCode from './base/QRCode'
// encode the app state to export into an array of chunks for the mobile app to understand.
// returned data frames are json stringified array with format: [ datalength, index, type, ...rest ]
// NB as soon as we have common types we'll move this in a ledgerhq/common project
function makeChunks(state: State): Array<string> {
const chunksFormatVersion = 1
const desktopVersion = __APP_VERSION__
const data = [
['meta', chunksFormatVersion, 'desktop', desktopVersion],
...accountsSelector(state).map(account => [
'account',
account.id,
account.name,
account.currency.id,
]),
]
return data.map((arr, i) => JSON.stringify([data.length, i, ...arr]))
}
const mapStateToProps = (state: State) => ({ chunks: makeChunks(state) })
const mapStateToProps = createSelector(accountsSelector, accounts => ({
chunks: makeChunks({
accounts,
exporterName: 'desktop',
exporterVersion: __APP_VERSION__,
pad: true,
}),
}))
class QRCodeExporter extends PureComponent<
{
@ -38,7 +28,7 @@ class QRCodeExporter extends PureComponent<
},
> {
static defaultProps = {
fps: 10,
fps: 4,
size: 480,
}

3
src/components/RecipientAddress/index.js

@ -101,9 +101,10 @@ class RecipientAddress extends PureComponent<Props, State> {
</Right>
) : null
const preOnChange = text => onChange((text && text.replace(/\s/g, '')) || '')
return (
<Box relative justifyContent="center">
<Input {...rest} value={value} onChange={onChange} renderRight={renderRight} />
<Input {...rest} value={value} onChange={preOnChange} renderRight={renderRight} />
</Box>
)
}

2
src/components/RenderError.js

@ -8,7 +8,7 @@ import { translate } from 'react-i18next'
import { urls } from 'config/urls'
import { i } from 'helpers/staticPath'
import hardReset from 'helpers/hardReset'
import { hardReset } from 'helpers/reset'
import type { T } from 'types/common'

20
src/components/SettingsPage/CleanButton.js

@ -4,12 +4,10 @@ import React, { Fragment, PureComponent } from 'react'
import { connect } from 'react-redux'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import { remote } from 'electron'
import { cleanAccountsCache } from 'actions/accounts'
import db from 'helpers/db'
import { delay } from 'helpers/promise'
import Button from 'components/base/Button'
import { ConfirmModal } from 'components/base/Modal'
import { softReset } from 'helpers/reset'
const mapDispatchToProps = {
cleanAccountsCache,
@ -22,11 +20,13 @@ type Props = {
type State = {
opened: boolean,
isLoading: boolean,
}
class CleanButton extends PureComponent<Props, State> {
state = {
opened: false,
isLoading: false,
}
open = () => this.setState({ opened: true })
@ -34,15 +34,18 @@ class CleanButton extends PureComponent<Props, State> {
close = () => this.setState({ opened: false })
action = async () => {
this.props.cleanAccountsCache()
await delay(500)
db.cleanCache()
remote.getCurrentWindow().webContents.reload()
if (this.state.isLoading) return
try {
this.setState({ isLoading: true })
await softReset({ cleanAccountsCache: this.props.cleanAccountsCache })
} finally {
this.setState({ isLoading: false })
}
}
render() {
const { t } = this.props
const { opened } = this.state
const { opened, isLoading } = this.state
return (
<Fragment>
<Button small primary onClick={this.open} event="ClearCacheIntent">
@ -55,6 +58,7 @@ class CleanButton extends PureComponent<Props, State> {
onClose={this.close}
onReject={this.close}
onConfirm={this.action}
isLoading={isLoading}
title={t('app:settings.softResetModal.title')}
subTitle={t('app:common.areYouSure')}
desc={t('app:settings.softResetModal.desc')}

2
src/components/SettingsPage/DevModeButton.js

@ -27,7 +27,7 @@ class DevModeButton extends PureComponent<Props> {
return (
<Fragment>
<Track onUpdate event={developerMode ? 'DevModeEnabled' : 'DevModeDisabled'} />
<Switch isChecked={developerMode} onChange={setDeveloperMode} />
<Switch isChecked={developerMode} onChange={setDeveloperMode} data-e2e="devMode_button" />
</Fragment>
)
}

4
src/components/SettingsPage/DisablePasswordModal.js

@ -1,7 +1,7 @@
// @flow
import React, { PureComponent } from 'react'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import db from 'helpers/db'
import Box from 'components/base/Box'
@ -12,8 +12,6 @@ import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'compone
import type { T } from 'types/common'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type Props = {
t: T,
onClose: Function,

4
src/components/SettingsPage/PasswordForm.js

@ -6,12 +6,10 @@ import Box from 'components/base/Box'
import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordsDontMatchError } from 'config/errors'
import type { T } from 'types/common'
const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch')
type Props = {
t: T,
hasPassword: boolean,

4
src/components/SettingsPage/PasswordModal.js

@ -5,15 +5,13 @@ import React, { PureComponent } from 'react'
import type { T } from 'types/common'
import db from 'helpers/db'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal'
import PasswordForm from './PasswordForm'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type Props = {
t: T,
onClose: () => void,

2
src/components/SettingsPage/ResetButton.js

@ -5,7 +5,7 @@ import styled from 'styled-components'
import { remote } from 'electron'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import hardReset from 'helpers/hardReset'
import { hardReset } from 'helpers/reset'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { ConfirmModal } from 'components/base/Modal'

2
src/components/SettingsPage/index.js

@ -106,7 +106,7 @@ class SettingsPage extends PureComponent<Props, State> {
return (
<Box pb={4} selectable>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5}>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5} data-e2e="settings_title">
{t('app:settings.title')}
</Box>
<Pills mb={4} items={items} activeKey={tab.key} onChange={this.handleChangeTab} />

2
src/components/TopBar/index.js

@ -109,7 +109,7 @@ class TopBar extends PureComponent<Props> {
</Box>
</Fragment>
)}
<Tooltip render={() => t('app:settings.title')}>
<Tooltip render={() => t('app:settings.title')} data-e2e="setting_button">
<ItemContainer isInteractive onClick={this.navigateToSettings}>
<IconSettings size={16} />
</ItemContainer>

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

@ -172,7 +172,7 @@ class InputCurrency extends PureComponent<Props, State> {
renderListUnits = () => {
const { units, onChangeUnit, unit } = this.props
const { isFocused } = this.state
const avoidEmptyValue = value => value && onChangeUnit(value)
if (units.length <= 1) {
return null
}
@ -180,13 +180,14 @@ class InputCurrency extends PureComponent<Props, State> {
return (
<Currencies onClick={stopPropagation}>
<Select
onChange={onChangeUnit}
onChange={avoidEmptyValue}
options={units}
value={unit}
getOptionValue={unitGetOptionValue}
renderOption={this.renderOption}
renderValue={this.renderValue}
fakeFocusRight={isFocused}
isRight
/>
</Currencies>
)

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

@ -28,6 +28,7 @@ class QRCode extends PureComponent<Props> {
qrcode.toCanvas(this._canvas, data, {
width: size,
margin: 0,
errorCorrectionLevel: 'Q',
color: {
light: '#ffffff00', // transparent background
},

6
src/components/base/Select/createStyles.js

@ -7,10 +7,14 @@ export default ({
width,
minWidth,
small,
isRight,
isLeft,
}: {
width: number,
minWidth: number,
small: boolean,
isRight: boolean,
isLeft: boolean,
}) => ({
control: (styles: Object, { isFocused }: Object) => ({
...styles,
@ -19,6 +23,8 @@ export default ({
...ff('Open Sans|SemiBold'),
height: small ? 34 : 40,
minHeight: 'unset',
borderRadius: isRight ? '0 4px 4px 0' : isLeft ? '4px 0 0 4px' : 4,
borderColor: colors.fog,
backgroundColor: 'white',
...(isFocused

6
src/components/base/Select/index.js

@ -21,6 +21,8 @@ type Props = {
placeholder: string,
isClearable: boolean,
isDisabled: boolean,
isRight: boolean,
isLeft: boolean,
isLoading: boolean,
isSearchable: boolean,
small: boolean,
@ -52,6 +54,8 @@ class Select extends PureComponent<Props> {
isSearchable,
isDisabled,
isLoading,
isRight,
isLeft,
placeholder,
options,
renderOption,
@ -69,7 +73,7 @@ class Select extends PureComponent<Props> {
classNamePrefix="select"
options={options}
components={createRenderers({ renderOption, renderValue })}
styles={createStyles({ width, minWidth, small })}
styles={createStyles({ width, minWidth, small, isRight, isLeft })}
placeholder={placeholder}
isDisabled={isDisabled}
isLoading={isLoading}

5
src/components/modals/AccountSettingRenderBody.js

@ -17,7 +17,7 @@ import { setDataModal } from 'reducers/modals'
import { getBridgeForCurrency } from 'bridge'
import { createCustomErrorClass } from 'helpers/errors'
import { AccountNameRequiredError, EnpointConfigError } from 'config/errors'
import TrackPage from 'analytics/TrackPage'
import Spoiler from 'components/base/Spoiler'
@ -36,9 +36,6 @@ import {
ConfirmModal,
} from 'components/base/Modal'
const AccountNameRequiredError = createCustomErrorClass('AccountNameRequired')
const EnpointConfigError = createCustomErrorClass('EnpointConfig')
type State = {
accountName: ?string,
accountUnit: ?Unit,

4
src/components/modals/Disclaimer.js

@ -25,7 +25,7 @@ class DisclaimerModal extends PureComponent<Props> {
name={MODAL_DISCLAIMER}
render={({ onClose }) => (
<ModalBody onClose={onClose}>
<ModalTitle>{t('app:disclaimerModal.title')}</ModalTitle>
<ModalTitle data-e2e="disclaimer_title">{t('app:disclaimerModal.title')}</ModalTitle>
<ModalContent flow={4} ff="Open Sans|Regular" fontSize={4} color="smoke">
<Box align="center" mt={4} pb={4}>
<HandShield size={55} />
@ -34,7 +34,7 @@ class DisclaimerModal extends PureComponent<Props> {
<p>{t('app:disclaimerModal.desc_2')}</p>
</ModalContent>
<ModalFooter horizontal justifyContent="flex-end">
<Button onClick={onClose} primary>
<Button data-e2e="continue_button" onClick={onClose} primary>
{t('app:disclaimerModal.cta')}
</Button>
</ModalFooter>

4
src/components/modals/UpdateFirmware/index.js

@ -10,7 +10,7 @@ import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority'
import type { StepProps as DefaultStepProps, Step } from 'components/base/Stepper'
import type { ModalStatus } from 'components/ManagerPage/FirmwareUpdate'
import type { LedgerScriptParams } from 'helpers/types'
import type { OsuFirmware } from 'helpers/types'
import { FreezeDeviceChangeEvents } from '../../ManagerPage/HookDeviceChange'
import StepFullFirmwareInstall from './steps/01-step-install-full-firmware'
@ -56,7 +56,7 @@ const createSteps = ({ t, shouldFlashMcu }: { t: T, shouldFlashMcu: boolean }):
return steps
}
export type Firmware = LedgerScriptParams & { shouldFlashMcu: boolean }
export type Firmware = OsuFirmware & { shouldFlashMcu: boolean }
export type StepProps = DefaultStepProps & {
firmware: Firmware,

36
src/config/errors.js

@ -4,15 +4,41 @@
import { createCustomErrorClass } from 'helpers/errors'
export const DisconnectedDevice = createCustomErrorClass('DisconnectedDevice')
export const UserRefusedOnDevice = createCustomErrorClass('UserRefusedOnDevice') // TODO rename because it's just for transaction refusal
export const AccountNameRequiredError = createCustomErrorClass('AccountNameRequired')
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export const CantOpenDevice = createCustomErrorClass('CantOpenDevice')
export const DeviceAppVerifyNotSupported = createCustomErrorClass('DeviceAppVerifyNotSupported')
export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress')
export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount')
export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
export const DeviceGenuineSocketEarlyClose = createCustomErrorClass('DeviceGenuineSocketEarlyClose')
export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
export const DeviceSocketFail = createCustomErrorClass('DeviceSocketFail')
export const DeviceSocketNoBulkStatus = createCustomErrorClass('DeviceSocketNoBulkStatus')
export const DeviceSocketNoHandler = createCustomErrorClass('DeviceSocketNoHandler')
export const DisconnectedDevice = createCustomErrorClass('DisconnectedDevice')
export const EnpointConfigError = createCustomErrorClass('EnpointConfig')
export const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed')
export const HardResetFail = createCustomErrorClass('HardResetFail')
export const InvalidAddress = createCustomErrorClass('InvalidAddress')
export const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')
export const LedgerAPIError = createCustomErrorClass('LedgerAPIError')
export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage')
export const LedgerAPINotAvailable = createCustomErrorClass('LedgerAPINotAvailable')
export const ManagerAppAlreadyInstalledError = createCustomErrorClass('ManagerAppAlreadyInstalled')
export const ManagerAppRelyOnBTCError = createCustomErrorClass('ManagerAppRelyOnBTC')
export const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
export const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
export const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
export const NetworkDown = createCustomErrorClass('NetworkDown')
export const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
export const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
export const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch')
export const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
export const TimeoutTagged = createCustomErrorClass('TimeoutTagged')
export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress')
export const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate')
export const UserRefusedOnDevice = createCustomErrorClass('UserRefusedOnDevice') // TODO rename because it's just for transaction refusal
export const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError')
export const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed')
export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount')
export const ETHAddressNonEIP = createCustomErrorClass('ETHAddressNonEIP')
export const CantScanQRCode = createCustomErrorClass('CantScanQRCode')

26
src/helpers/apps/installApp.js

@ -1,18 +1,17 @@
// @flow
import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
import { WS_INSTALL } from 'helpers/urls'
import { createCustomErrorClass } from 'helpers/errors'
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerAppAlreadyInstalledError = createCustomErrorClass('ManagerAppAlreadyInstalled')
const ManagerAppRelyOnBTCError = createCustomErrorClass('ManagerAppRelyOnBTC')
import {
ManagerNotEnoughSpaceError,
ManagerDeviceLockedError,
ManagerAppAlreadyInstalledError,
ManagerAppRelyOnBTCError,
} from 'config/errors'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -37,8 +36,8 @@ function remapError(promise) {
export default async function installApp(
transport: Transport<*>,
targetId: string | number,
{ app }: { app: LedgerScriptParams },
): Promise<*> {
{ app }: { app: ApplicationVersion },
): Promise<void> {
const params = {
targetId,
perso: app.perso,
@ -47,6 +46,7 @@ export default async function installApp(
firmwareKey: app.firmware_key,
hash: app.hash,
}
const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise())
const url = WS_INSTALL(params)
await remapError(createDeviceSocket(transport, url).toPromise())
}

15
src/helpers/apps/listAppVersions.js

@ -1,14 +1,19 @@
// @flow
import network from 'api/network'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, DeviceVersion, FinalFirmware, ApplicationVersion } from 'helpers/types'
import { APPLICATIONS_BY_DEVICE } from 'helpers/urls'
import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware'
export default async (deviceInfo: DeviceInfo) => {
const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmwareData = await getCurrentFirmware({
type NetworkResponse = { data: { application_versions: Array<ApplicationVersion> } }
export default async (deviceInfo: DeviceInfo): Promise<Array<ApplicationVersion>> => {
const deviceData: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
const firmwareData: FinalFirmware = await getCurrentFirmware({
deviceId: deviceData.id,
fullVersion: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
@ -20,6 +25,6 @@ export default async (deviceInfo: DeviceInfo) => {
}
const {
data: { application_versions },
} = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
}: NetworkResponse = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
return application_versions.length > 0 ? application_versions : []
}

3
src/helpers/apps/listApps.js

@ -2,8 +2,9 @@
import network from 'api/network'
import { GET_APPLICATIONS } from 'helpers/urls'
import type { Application } from 'helpers/types'
export default async () => {
export default async (): Promise<Array<Application>> => {
const { data } = await network({ method: 'GET', url: GET_APPLICATIONS })
return data.length > 0 ? data : []
}

5
src/helpers/apps/listCategories.js

@ -2,8 +2,9 @@
import network from 'api/network'
import { GET_CATEGORIES } from 'helpers/urls'
import type { Category } from 'helpers/types'
export default async () => {
const { data } = await network({ method: 'GET', url: GET_CATEGORIES })
export default async (): Promise<Array<Category>> => {
const { data }: { data: Array<Category> } = await network({ method: 'GET', url: GET_CATEGORIES })
return data.length > 0 ? data : []
}

18
src/helpers/apps/uninstallApp.js

@ -1,15 +1,11 @@
// @flow
import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/types'
import { createCustomErrorClass } from '../errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
import type { ApplicationVersion } from 'helpers/types'
import { ManagerDeviceLockedError, ManagerUninstallBTCDep } from 'config/errors'
import { WS_INSTALL } from 'helpers/urls'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -30,8 +26,8 @@ function remapError(promise) {
export default async function uninstallApp(
transport: Transport<*>,
targetId: string | number,
{ app }: { app: LedgerScriptParams },
): Promise<*> {
{ app }: { app: ApplicationVersion },
): Promise<void> {
const params = {
targetId,
perso: app.perso,
@ -40,6 +36,6 @@ export default async function uninstallApp(
firmwareKey: app.delete_key,
hash: app.hash,
}
const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise())
const url = WS_INSTALL(params)
await remapError(createDeviceSocket(transport, url).toPromise())
}

3
src/helpers/debugAppInfosForCurrency/btc.js

@ -1,9 +1,6 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import { createCustomErrorClass } from '../errors'
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export default async (transport: Transport<*>) => {
const r = await transport.send(0xe0, 0xc4, 0, 0)

5
src/helpers/devices/getCurrentFirmware.js

@ -2,6 +2,7 @@
import network from 'api/network'
import { GET_CURRENT_FIRMWARE } from 'helpers/urls'
import type { FinalFirmware } from 'helpers/types'
type Input = {
fullVersion: string,
@ -9,8 +10,8 @@ type Input = {
provider: number,
}
export default async (input: Input): Promise<*> => {
const { data } = await network({
export default async (input: Input): Promise<FinalFirmware> => {
const { data }: { data: FinalFirmware } = await network({
method: 'POST',
url: GET_CURRENT_FIRMWARE,
data: {

12
src/helpers/devices/getDeviceInfo.js

@ -5,17 +5,7 @@ import type Transport from '@ledgerhq/hw-transport'
import getFirmwareInfo from 'helpers/firmware/getFirmwareInfo'
import { FORCE_PROVIDER } from 'config/constants'
export type DeviceInfo = {
targetId: string | number,
seVersion: string,
isBootloader: boolean,
flags: string,
mcuVersion: string,
isOSU: boolean,
providerName: string,
providerId: number,
fullVersion: string,
}
import type { DeviceInfo } from 'helpers/types'
const PROVIDERS = {
'': 1,

6
src/helpers/devices/getDeviceVersion.js

@ -2,8 +2,10 @@
import { GET_DEVICE_VERSION } from 'helpers/urls'
import network from 'api/network'
export default async (targetId: string | number, provider: number): Promise<*> => {
const { data } = await network({
import type { DeviceVersion } from 'helpers/types'
export default async (targetId: string | number, provider: number): Promise<DeviceVersion> => {
const { data }: { data: DeviceVersion } = await network({
method: 'POST',
url: GET_DEVICE_VERSION,
data: {

12
src/helpers/devices/getIsGenuine.js

@ -2,23 +2,29 @@
import type Transport from '@ledgerhq/hw-transport'
import { SKIP_GENUINE } from 'config/constants'
import { WS_GENUINE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, FinalFirmware, DeviceVersion } from 'helpers/types'
import { createDeviceSocket } from 'helpers/socket'
import getCurrentFirmware from './getCurrentFirmware'
import getDeviceVersion from './getDeviceVersion'
export default async (transport: Transport<*>, deviceInfo: DeviceInfo): Promise<string> => {
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware = await getCurrentFirmware({
const deviceVersion: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
const firmware: FinalFirmware = await getCurrentFirmware({
deviceId: deviceVersion.id,
fullVersion: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
})
const params = {
targetId: deviceInfo.targetId,
perso: firmware.perso,
}
const url = WS_GENUINE(params)
return SKIP_GENUINE
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000))

36
src/helpers/devices/getLatestFirmwareForDevice.js

@ -1,7 +1,13 @@
// @flow
import network from 'api/network'
import { GET_LATEST_FIRMWARE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type {
DeviceInfo,
DeviceVersion,
FinalFirmware,
OsuFirmware,
McuVersion,
} from 'helpers/types'
import getFinalFirmwareById from 'helpers/firmware/getFinalFirmwareById'
import getMcus from 'helpers/firmware/getMcus'
@ -9,19 +15,31 @@ import getMcus from 'helpers/firmware/getMcus'
import getCurrentFirmware from './getCurrentFirmware'
import getDeviceVersion from './getDeviceVersion'
export default async (deviceInfo: DeviceInfo) => {
type NetworkResponse = {
data: {
result: string,
se_firmware_osu_version: OsuFirmware,
},
}
type Result = ?(OsuFirmware & { shouldFlashMcu: boolean })
export default async (deviceInfo: DeviceInfo): Promise<Result> => {
// Get device infos from targetId
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const deviceVersion: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
// Get firmware infos with firmware name and device version
const seFirmwareVersion = await getCurrentFirmware({
const seFirmwareVersion: FinalFirmware = await getCurrentFirmware({
fullVersion: deviceInfo.fullVersion,
deviceId: deviceVersion.id,
provider: deviceInfo.providerId,
})
// Fetch next possible firmware
const { data } = await network({
const { data }: NetworkResponse = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
@ -37,11 +55,13 @@ export default async (deviceInfo: DeviceInfo) => {
const { se_firmware_osu_version } = data
const { next_se_firmware_final_version } = se_firmware_osu_version
const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version)
const seFirmwareFinalVersion: FinalFirmware = await getFinalFirmwareById(
next_se_firmware_final_version,
)
const mcus = await getMcus()
const mcus: Array<McuVersion> = await getMcus()
const currentMcuVersionId = mcus
const currentMcuVersionId: Array<number> = mcus
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)

2
src/helpers/devices/shouldFlashMcu.js

@ -1,7 +1,7 @@
// @flow
import network from 'api/network'
import { GET_LATEST_FIRMWARE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import getFinalFirmwareById from 'helpers/firmware/getFinalFirmwareById'
import getMcus from 'helpers/firmware/getMcus'

3
src/helpers/firmware/getFirmwareInfo.js

@ -1,6 +1,7 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import type { FirmwareInfo } from 'helpers/types'
const APDUS = {
GET_FIRMWARE: [0xe0, 0x01, 0x00, 0x00],
@ -12,7 +13,7 @@ const APDUS = {
/**
* Retrieve targetId and firmware version from device
*/
export default async function getFirmwareInfo(transport: Transport<*>) {
export default async function getFirmwareInfo(transport: Transport<*>): Promise<FirmwareInfo> {
const res = await transport.send(...APDUS.GET_FIRMWARE)
const byteArray = [...res]
const data = byteArray.slice(0, byteArray.length - 2)

7
src/helpers/firmware/getNextMCU.js

@ -2,12 +2,13 @@
import network from 'api/network'
import { GET_NEXT_MCU } from 'helpers/urls'
import { createCustomErrorClass } from 'helpers/errors'
import type { OsuFirmware } from 'helpers/types'
import { LatestMCUInstalledError } from 'config/errors'
const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')
type NetworkResponse = { data: OsuFirmware | 'default' }
export default async (bootloaderVersion: string): Promise<*> => {
const { data } = await network({
const { data }: NetworkResponse = await network({
method: 'POST',
url: GET_NEXT_MCU,
data: {

15
src/helpers/firmware/installFinalFirmware.js

@ -1,16 +1,15 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, DeviceVersion, OsuFirmware, FinalFirmware } from 'helpers/types'
import { WS_INSTALL } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket'
import { createCustomErrorClass } from 'helpers/errors'
import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getOsuFirmware from 'helpers/devices/getOsuFirmware'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import getFinalFirmwareById from './getFinalFirmwareById'
import { ManagerDeviceLockedError } from 'config/errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
import getFinalFirmwareById from './getFinalFirmwareById'
function remapSocketError(promise) {
return promise.catch((e: Error) => {
@ -23,19 +22,19 @@ function remapSocketError(promise) {
})
}
type Result = Promise<{ success: boolean, error?: string }>
type Result = Promise<{ success: boolean }>
export default async (transport: Transport<*>): Result => {
try {
const deviceInfo: DeviceInfo = await getDeviceInfo(transport)
const device = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware = await getOsuFirmware({
const device: DeviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware: OsuFirmware = await getOsuFirmware({
deviceId: device.id,
version: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
})
const { next_se_firmware_final_version } = firmware
const nextFirmware = await getFinalFirmwareById(next_se_firmware_final_version)
const nextFirmware: FinalFirmware = await getFinalFirmwareById(next_se_firmware_final_version)
const params = {
targetId: deviceInfo.targetId,

11
src/helpers/firmware/installMcu.js

@ -5,9 +5,9 @@ import { WS_MCU } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket'
import getNextMCU from 'helpers/firmware/getNextMCU'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import { createCustomErrorClass } from 'helpers/errors'
import { ManagerDeviceLockedError } from 'config/errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
import type { DeviceInfo } from 'helpers/types'
function remapSocketError(promise) {
return promise.catch((e: Error) => {
@ -20,16 +20,15 @@ function remapSocketError(promise) {
})
}
type Result = Promise<*>
type Result = Promise<void>
export default async (transport: Transport<*>): Result => {
const deviceInfo = await getDeviceInfo(transport)
const { seVersion: version, targetId } = deviceInfo
const { seVersion: version, targetId }: DeviceInfo = await getDeviceInfo(transport)
const nextVersion = await getNextMCU(version)
const params = {
targetId,
version: nextVersion.name,
}
const url = WS_MCU(params)
return remapSocketError(createDeviceSocket(transport, url).toPromise())
await remapSocketError(createDeviceSocket(transport, url).toPromise())
}

12
src/helpers/firmware/installOsuFirmware.js

@ -6,11 +6,11 @@ import { createDeviceSocket } from 'helpers/socket'
import type { Firmware } from 'components/modals/UpdateFirmware'
import { createCustomErrorClass } from '../errors'
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate')
import {
ManagerNotEnoughSpaceError,
ManagerDeviceLockedError,
UserRefusedFirmwareUpdate,
} from 'config/errors'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -27,7 +27,7 @@ function remapError(promise) {
})
}
type Result = Promise<{ success: boolean, error?: any }>
type Result = Promise<{ success: boolean }>
export default async (
transport: Transport<*>,

4
src/helpers/getAddressForCurrency/btc.js

@ -3,10 +3,8 @@
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import Btc from '@ledgerhq/hw-app-btc'
import type Transport from '@ledgerhq/hw-transport'
import { BtcUnmatchedApp } from 'config/errors'
import getBitcoinLikeInfo from '../devices/getBitcoinLikeInfo'
import { createCustomErrorClass } from '../errors'
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export default async (
transport: Transport<*>,

13
src/helpers/hardReset.js

@ -1,13 +0,0 @@
import libcoreHardReset from 'commands/libcoreHardReset'
import { disable as disableDBMiddleware } from 'middlewares/db'
import db from 'helpers/db'
import { delay } from 'helpers/promise'
export default async function hardReset() {
await libcoreHardReset.send()
disableDBMiddleware()
db.resetAll()
await delay(500)
window.location.href = ''
}

9
src/helpers/libcore.js

@ -14,12 +14,11 @@ import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgerc
import { isSegwitPath, isUnsplitPath } from 'helpers/bip32'
import * as accountIdHelper from 'helpers/accountId'
import { createCustomErrorClass, deserializeError } from './errors'
import { NoAddressesFound } from 'config/errors'
import { deserializeError } from './errors'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName'
import { timeoutTagged } from './promise'
const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
// TODO: put that info inside currency itself
const SPLITTED_CURRENCIES = {
bitcoin_cash: {
@ -272,7 +271,7 @@ async function scanNextAccount(props: {
const shouldSyncAccount = true // TODO: let's sync everytime. maybe in the future we can optimize.
if (shouldSyncAccount) {
await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount))
await coreSyncAccount(core, njsAccount)
}
if (isUnsubscribed()) return []
@ -550,7 +549,7 @@ export async function syncAccount({
)
}
const unsub = await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount))
const unsub = await coreSyncAccount(core, njsAccount)
unsub()
const query = njsAccount.queryOperations()

35
src/helpers/reset.js

@ -0,0 +1,35 @@
// @flow
import path from 'path'
import rimraf from 'rimraf'
import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory'
import { disable as disableDBMiddleware } from 'middlewares/db'
import db from 'helpers/db'
import { delay } from 'helpers/promise'
function resetLibcoreDatabase() {
const dbpath = path.resolve(resolveUserDataDirectory(), 'sqlite/')
rimraf.sync(dbpath, { glob: false })
}
function reload() {
require('electron')
.remote.getCurrentWindow()
.webContents.reload()
}
export async function hardReset() {
resetLibcoreDatabase()
disableDBMiddleware()
db.resetAll()
await delay(500)
reload()
}
export async function softReset({ cleanAccountsCache }: *) {
resetLibcoreDatabase()
cleanAccountsCache()
await delay(500)
db.cleanCache()
reload()
}

14
src/helpers/socket.js

@ -5,13 +5,13 @@ import logger from 'logger'
import Websocket from 'ws'
import type Transport from '@ledgerhq/hw-transport'
import { Observable } from 'rxjs'
import { createCustomErrorClass } from './errors'
const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError')
const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed')
const DeviceSocketFail = createCustomErrorClass('DeviceSocketFail')
const DeviceSocketNoBulkStatus = createCustomErrorClass('DeviceSocketNoBulkStatus')
const DeviceSocketNoHandler = createCustomErrorClass('DeviceSocketNoHandler')
import {
WebsocketConnectionError,
WebsocketConnectionFailed,
DeviceSocketFail,
DeviceSocketNoBulkStatus,
DeviceSocketNoHandler,
} from 'config/errors'
/**
* use Ledger WebSocket API to exchange data with the device

132
src/helpers/types.js

@ -1,14 +1,134 @@
// @flow
type Id = number
export type LedgerScriptParams = {
firmware?: string,
firmware_key?: string,
firmware: string,
firmwareKey: string,
delete?: string,
delete_key?: string,
deleteKey?: string,
targetId?: string | number,
hash: string,
perso: string,
}
export type DeviceInfo = {
targetId: string | number,
seVersion: string,
isBootloader: boolean,
flags: string,
mcuVersion: string,
isOSU: boolean,
providerName: string,
providerId: number,
fullVersion: string,
}
export type DeviceVersion = {
id: Id,
name: string,
display_name: string,
target_id: string,
description: string,
device: Id,
providers: Array<Id>,
mcu_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
osu_versions: Array<Id>,
application_versions: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type McuVersion = {
id: Id,
mcu: Id,
name: string,
description: ?string,
providers: Array<Id>,
from_bootloader_version: string,
device_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type FirmwareInfo = {
targetId: Id,
seVersion: string,
flags: string,
mcuVersion: string,
}
type BaseFirmware = {
id: Id,
name: string,
description: ?string,
display_name: ?string,
notes: ?string,
perso: string,
firmware: string,
firmware_key: string,
hash: string,
date_creation: string,
date_last_modified: string,
device_versions: Array<Id>,
providers: Array<Id>,
}
export type OsuFirmware = BaseFirmware & {
next_se_firmware_final_version: Id,
previous_se_firmware_final_version: Array<Id>,
}
export type FinalFirmware = BaseFirmware & {
version: string,
se_firmware: Id,
osu_versions: Array<OsuFirmware>,
mcu_versions: Array<Id>,
application_versions: Array<Id>,
}
export type ApplicationVersion = {
id: Id,
name: string,
version: string,
app: Id,
description: ?string,
display_name: string,
icon: string,
app?: number,
hash?: string,
perso?: string,
picture: Id,
notes: ?string,
perso: string,
hash: string,
firmware: string,
firmware_key: string,
delete: string,
delete_key: string,
device_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
providers: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type Application = {
id: Id,
name: string,
description: ?string,
application_versions: Array<ApplicationVersion>,
providers: Array<Id>,
category: Id,
publisher: ?Id,
date_creation: string,
date_last_modified: string,
}
export type Category = {
id: Id,
name: string,
description: ?string,
providers: Array<Id>,
applications: Array<Id>,
date_creation: string,
date_last_modified: string,
}

4
static/i18n/en/errors.json

@ -72,8 +72,8 @@
"description": "Check your device to see which apps are already installed."
},
"ManagerAppRelyOnBTC": {
"title": "Bitcoin or Ethereum app required",
"description": "Either install the latest Ethereum app (for ETC/UBIQ/EXP/RSK/WAN/kUSD/POA), or the latest Bitcoin app."
"title": "Bitcoin and Ethereum apps required",
"description": "Install the latest Bitcoin and Ethereum apps first."
},
"ManagerDeviceLocked": {
"title": "Please unlock your device",

64
test-e2e/nav_to_settings.spec.js

@ -0,0 +1,64 @@
const Application = require('spectron').Application
let app
const TIMEOUT = 50 * 1000
describe('Application launch', () => {
beforeEach(async () => {
app = new Application({
path: './dist/ledger-live-desktop-1.1.0-linux-x86_64.AppImage',
env: {
SKIP_ONBOARDING: '1',
},
})
await app.start()
}, TIMEOUT)
afterEach(async () => {
if (app && app.isRunning()) {
await app.stop()
}
}, TIMEOUT)
test(
'Start app and set developper mode ',
async () => {
const title = await app.client.getTitle()
expect(title).toEqual('Ledger Live')
await app.client.waitUntilWindowLoaded()
await app.client.pause(2000)
// Post Onboarding
const title_onboarding = await app.client.getText('[data-e2e=onboarding_title]')
expect(title_onboarding).toEqual('Analytics and bug reports')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
const title_finish = await app.client.getText('[data-e2e=finish_title]')
expect(title_finish).toEqual('Your device is ready!')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
const title_disclaimer = await app.client.getText('[data-e2e=disclaimer_title]')
expect(title_disclaimer).toEqual('Trade safely')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
// Dashboard EmptyState
const title_dashboard_empty = await app.client.getText('[data-e2e=dashboard_empty_title]')
expect(title_dashboard_empty).toEqual('Add accounts to your portfolio')
// Open Settings
await app.client.click('[data-e2e=setting_button]')
await app.client.pause(1000)
const title_settings = await app.client.getText('[data-e2e=settings_title]')
expect(title_settings).toEqual('Settings')
// DevMode ON
await app.client.click('[data-e2e=devMode_button]')
await app.client.pause(500)
},
TIMEOUT,
)
})

306
yarn.lock

@ -1474,6 +1474,13 @@
version "0.7.1"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.7.1.tgz#e44e596d03c9f16ba3b127ad333a8a072bcb5a0a"
"@gimenete/type-writer@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@gimenete/type-writer/-/type-writer-0.1.3.tgz#2d4f26118b18d71f5b34ca24fdd6d1fd455c05b6"
dependencies:
camelcase "^5.0.0"
prettier "^1.13.7"
"@ledgerhq/hw-app-btc@4.21.0":
version "4.21.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.21.0.tgz#4f94571bb3d63cd785e31a7e1f77ce597c344516"
@ -1542,9 +1549,9 @@
npm "^5.7.1"
prebuild-install "^2.2.2"
"@ledgerhq/live-common@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.0.2.tgz#1ee5fcc6044c5a049c067978d81892f79789863c"
"@ledgerhq/live-common@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-3.3.0.tgz#e4e798f5bfee8e788094fab8dc11fe957a750544"
dependencies:
axios "^0.18.0"
bignumber.js "^7.2.1"
@ -1568,6 +1575,20 @@
version "1.1.0"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz#50c1e2260ac0ed9439a181de3725a0168d59c48a"
"@octokit/rest@^15.10.0":
version "15.10.0"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-15.10.0.tgz#9baf7430e55edf1a1024c35ae72ed2f5fc6e90e9"
dependencies:
"@gimenete/type-writer" "^0.1.3"
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"
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"
@ -2213,6 +2234,30 @@ aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2, aproba@~1.2.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
archiver-utils@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174"
dependencies:
glob "^7.0.0"
graceful-fs "^4.1.0"
lazystream "^1.0.0"
lodash "^4.8.0"
normalize-path "^2.0.0"
readable-stream "^2.0.0"
archiver@~2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-2.1.1.tgz#ff662b4a78201494a3ee544d3a33fe7496509ebc"
dependencies:
archiver-utils "^1.3.0"
async "^2.0.0"
buffer-crc32 "^0.2.1"
glob "^7.0.0"
lodash "^4.8.0"
readable-stream "^2.0.0"
tar-stream "^1.5.0"
zip-stream "^1.2.0"
archy@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
@ -2414,7 +2459,7 @@ async@^1.4.0, async@^1.5.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
async@^2.1.2, async@^2.1.4, async@^2.6.0, async@^2.6.1:
async@^2.0.0, async@^2.1.2, async@^2.1.4, async@^2.6.0, async@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
@ -3560,6 +3605,10 @@ bech32@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.3.tgz#bd47a8986bbb3eec34a56a097a84b8d3e9a2dfcd"
before-after-hook@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.1.0.tgz#83165e15a59460d13702cb8febd6a1807896db5a"
bfj-node4@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.3.1.tgz#e23d8b27057f1d0214fc561142ad9db998f26830"
@ -3878,6 +3927,10 @@ bser@^2.0.0:
dependencies:
node-int64 "^0.4.0"
btoa-lite@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@ -3889,6 +3942,10 @@ buffer-alloc@^1.1.0:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@^0.2.1:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
@ -3928,6 +3985,13 @@ buffer@^5.0.3:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.0.tgz#53cf98241100099e9eeae20ee6d51d21b16e541e"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffers@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
@ -4127,6 +4191,10 @@ camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
camelcase@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
can-promise@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/can-promise/-/can-promise-0.0.1.tgz#7a7597ad801fb14c8b22341dfec314b6bd6ad8d3"
@ -4612,6 +4680,15 @@ component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
compress-commons@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.2.tgz#524a9f10903f3a813389b0225d27c48bb751890f"
dependencies:
buffer-crc32 "^0.2.1"
crc32-stream "^2.0.0"
normalize-path "^2.0.0"
readable-stream "^2.0.0"
compressible@~2.0.13:
version "2.0.14"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7"
@ -4774,6 +4851,19 @@ cosmiconfig@^4.0.0:
parse-json "^4.0.0"
require-from-string "^2.0.1"
crc32-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4"
dependencies:
crc "^3.4.4"
readable-stream "^2.0.0"
crc@^3.4.4:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
dependencies:
buffer "^5.1.0"
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@ -4935,6 +5025,12 @@ css-loader@^0.28.11:
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
css-parse@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4"
dependencies:
css "^2.0.0"
css-select-base-adapter@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz#0102b3d14630df86c3eb9fa9f5456270106cf990"
@ -4991,10 +5087,23 @@ css-url-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec"
css-value@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea"
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
css@^2.0.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be"
dependencies:
inherits "^2.0.1"
source-map "^0.1.38"
source-map-resolve "^0.5.1"
urix "^0.1.0"
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
@ -5383,6 +5492,10 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312"
default-require-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7"
@ -5508,6 +5621,10 @@ detect-port-alt@1.1.6:
address "^1.0.1"
debug "^2.6.0"
dev-null@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dev-null/-/dev-null-0.1.1.tgz#5a205ce3c2b2ef77b6238d6ba179eb74c6a0e818"
dezalgo@^1.0.0, dezalgo@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
@ -5754,6 +5871,10 @@ ejs@^2.5.7, ejs@^2.5.9, ejs@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
ejs@~2.5.6:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
electron-builder-lib@20.14.7, electron-builder-lib@~20.14.6:
version "20.14.7"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-20.14.7.tgz#db91977dd13b0a288e1da5629183807a9847de21"
@ -5803,6 +5924,13 @@ electron-builder@20.14.7:
update-notifier "^2.5.0"
yargs "^11.0.0"
electron-chromedriver@~1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-1.8.0.tgz#901714133cf6f6093d365e1f44a52d99624d8241"
dependencies:
electron-download "^4.1.0"
extract-zip "^1.6.5"
electron-context-menu@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-0.10.0.tgz#97fce2b805e03ac2b1dae11eb6a68b064b78d633"
@ -5855,6 +5983,20 @@ electron-download@^3.0.1:
semver "^5.3.0"
sumchecker "^1.2.0"
electron-download@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.0.tgz#bf932c746f2f87ffcc09d1dd472f2ff6b9187845"
dependencies:
debug "^2.2.0"
env-paths "^1.0.0"
fs-extra "^2.0.0"
minimist "^1.2.0"
nugget "^2.0.0"
path-exists "^3.0.0"
rc "^1.1.2"
semver "^5.3.0"
sumchecker "^2.0.1"
electron-is-dev@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
@ -5963,9 +6105,9 @@ electron-webpack@^2.1.0:
webpack-merge "^4.1.2"
yargs "^11.1.0"
electron@1.8.7:
version "1.8.7"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.7.tgz#373c1dc4589d7ab4acd49aff8db4a1c0a6c3bcc1"
electron@1.8.8:
version "1.8.8"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.8.tgz#a90cddb075291f49576993e6f5c8bb4439301cae"
dependencies:
"@types/node" "^8.0.24"
electron-download "^3.0.1"
@ -6632,7 +6774,7 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@^1.0.3:
extract-zip@^1.0.3, extract-zip@^1.6.5:
version "1.6.7"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
dependencies:
@ -6994,6 +7136,13 @@ fs-extra@^0.30.0:
path-is-absolute "^1.0.0"
rimraf "^2.2.8"
fs-extra@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^2.1.0"
fs-extra@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
@ -7102,6 +7251,12 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gaze@~1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
dependencies:
globule "^1.0.0"
genfun@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/genfun/-/genfun-4.0.1.tgz#ed10041f2e4a7f1b0a38466d17a5c3e27df1dfc1"
@ -7222,7 +7377,7 @@ glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.2:
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1, glob@~7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@ -7305,6 +7460,14 @@ globby@^8.0.0, globby@^8.0.1:
pify "^3.0.0"
slash "^1.0.0"
globule@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
dependencies:
glob "~7.1.1"
lodash "~4.17.10"
minimatch "~3.0.2"
got@^6.7.1:
version "6.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
@ -7362,7 +7525,7 @@ got@^8.3.1:
url-parse-lax "^3.0.0"
url-to-options "^1.0.1"
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.11:
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.11:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@ -7949,7 +8112,7 @@ inline-style-prefixer@^3.0.6:
bowser "^1.7.3"
css-in-js-utils "^2.0.0"
inquirer@3.3.0, inquirer@^3.0.6:
inquirer@3.3.0, inquirer@^3.0.6, inquirer@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
dependencies:
@ -9075,6 +9238,12 @@ lazy-val@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
lazystream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
dependencies:
readable-stream "^2.0.5"
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
@ -9354,7 +9523,7 @@ lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1:
lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.8.0, lodash@~4.17.10:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@ -9767,7 +9936,7 @@ minimatch@3.0.3:
dependencies:
brace-expansion "^1.0.0"
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@ -9996,6 +10165,10 @@ node-fetch@^1.0.1:
encoding "^0.1.11"
is-stream "^1.0.1"
node-fetch@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.2.0.tgz#4ee79bde909262f9775f731e3656d0db55ced5b5"
node-forge@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
@ -10136,7 +10309,7 @@ normalize-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
normalize-path@^2.0.1, normalize-path@^2.1.1:
normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
@ -10184,6 +10357,10 @@ npm-install-checks@~3.0.0:
dependencies:
semver "^2.3.0 || 3.x || 4 || 5"
npm-install-package@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/npm-install-package/-/npm-install-package-2.1.0.tgz#d7efe3cfcd7ab00614b896ea53119dc9ab259125"
npm-lifecycle@^2.0.1, npm-lifecycle@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.0.3.tgz#696bedf1143371163e9cc16fe872357e25d8d90e"
@ -10556,7 +10733,7 @@ opn@^5.1.0:
dependencies:
is-wsl "^1.1.0"
optimist@^0.6.1:
optimist@^0.6.1, optimist@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
@ -11381,6 +11558,10 @@ prettier@^1.12.1, prettier@^1.13.5:
version "1.13.7"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
prettier@^1.13.7:
version "1.14.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9"
pretty-bytes@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84"
@ -11565,7 +11746,7 @@ pushdata-bitcoin@^1.0.1:
dependencies:
bitcoin-ops "^1.3.0"
q@^1.1.2:
q@^1.1.2, q@~1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -12140,7 +12321,7 @@ read@1, read@~1.0.1, read@~1.0.7:
dependencies:
mute-stream "~0.0.4"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6:
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
@ -12516,7 +12697,7 @@ request-promise-native@^1.0.5:
tunnel-agent "^0.6.0"
uuid "^3.0.0"
request@^2.45.0, request@^2.74.0, request@^2.83.0, request@^2.85.0:
request@^2.45.0, request@^2.74.0, request@^2.81.0, request@^2.83.0, request@^2.85.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
dependencies:
@ -12647,6 +12828,10 @@ retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
rgb2hex@~0.1.4:
version "0.1.9"
resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.1.9.tgz#5d3e0e14b0177b568e6f0d5b43e34fbfdb670346"
right-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@ -13233,7 +13418,7 @@ source-list-map@~0.1.7:
version "0.1.8"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
source-map-resolve@^0.5.0:
source-map-resolve@^0.5.0, source-map-resolve@^0.5.1:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
dependencies:
@ -13268,6 +13453,12 @@ source-map@0.7.3, source-map@^0.7.2:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
source-map@^0.1.38:
version "0.1.43"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
dependencies:
amdefine ">=0.0.4"
source-map@^0.4.4, source-map@~0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
@ -13335,6 +13526,16 @@ spdy@^3.4.1:
select-hose "^2.0.0"
spdy-transport "^2.0.18"
spectron@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/spectron/-/spectron-3.8.0.tgz#122c3562fd7e92b7cdf6f94094aa495b150dfa51"
dependencies:
dev-null "^0.1.1"
electron-chromedriver "~1.8.0"
request "^2.81.0"
split "^1.0.0"
webdriverio "^4.8.0"
speedometer@~0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d"
@ -13345,6 +13546,12 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
dependencies:
through "2"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -13641,7 +13848,7 @@ sumchecker@^1.2.0:
debug "^2.2.0"
es6-promise "^4.0.5"
sumchecker@^2.0.2:
sumchecker@^2.0.1, sumchecker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e"
dependencies:
@ -13673,6 +13880,12 @@ supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
dependencies:
has-flag "^3.0.0"
supports-color@~5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.0.1.tgz#1c5331f22250c84202805b2f17adf16699f3a39a"
dependencies:
has-flag "^2.0.0"
svg-tag-names@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/svg-tag-names/-/svg-tag-names-1.1.1.tgz#9641b29ef71025ee094c7043f7cdde7d99fbd50a"
@ -13765,7 +13978,7 @@ tar-fs@^1.13.0:
pump "^1.0.0"
tar-stream "^1.1.2"
tar-stream@^1.1.2:
tar-stream@^1.1.2, tar-stream@^1.5.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
dependencies:
@ -13863,7 +14076,7 @@ through2@~0.2.3:
readable-stream "~1.1.9"
xtend "~2.1.1"
"through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3.6:
through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -14343,11 +14556,15 @@ url-parse@^1.1.8, url-parse@~1.4.0:
querystringify "^2.0.0"
requires-port "^1.0.0"
url-template@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
url@^0.11.0:
url@^0.11.0, url@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
dependencies:
@ -14584,6 +14801,36 @@ wcwidth@^1.0.0:
dependencies:
defaults "^1.0.3"
wdio-dot-reporter@~0.0.8:
version "0.0.10"
resolved "https://registry.yarnpkg.com/wdio-dot-reporter/-/wdio-dot-reporter-0.0.10.tgz#facfb7c9c5984149951f59cbc3cd0752101cf0e0"
webdriverio@^4.8.0:
version "4.13.1"
resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-4.13.1.tgz#624ef4ca569f3c9a5e8e9b11302b4431eda1fb8a"
dependencies:
archiver "~2.1.0"
babel-runtime "^6.26.0"
css-parse "^2.0.0"
css-value "~0.0.1"
deepmerge "~2.0.1"
ejs "~2.5.6"
gaze "~1.1.2"
glob "~7.1.1"
inquirer "~3.3.0"
json-stringify-safe "~5.0.1"
mkdirp "~0.5.1"
npm-install-package "~2.1.0"
optimist "~0.6.1"
q "~1.5.0"
request "^2.83.0"
rgb2hex "~0.1.4"
safe-buffer "~5.1.1"
supports-color "~5.0.0"
url "~0.11.0"
wdio-dot-reporter "~0.0.8"
wgxpath "~1.0.0"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@ -14810,6 +15057,10 @@ websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
wgxpath@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690"
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3"
@ -15280,6 +15531,15 @@ yeoman-generator@^2.0.5:
through2 "^2.0.0"
yeoman-environment "^2.0.5"
zip-stream@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"
dependencies:
archiver-utils "^1.3.0"
compress-commons "^1.2.0"
lodash "^4.8.0"
readable-stream "^2.0.0"
zxcvbn@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"

Loading…
Cancel
Save