Browse Source

Merge pull request #929 from LedgerHQ/develop

Prepare for 1.0.0
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
90cb5e49d9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 4
      .github/ISSUE_TEMPLATE/discussion.md
  3. 18
      .github/ISSUE_TEMPLATE/enhancement.md
  4. 14
      .github/ISSUE_TEMPLATE/feature_request.md
  5. 2
      .github/ISSUE_TEMPLATE/question.md
  6. 29
      README.md
  7. 0
      docs/architecture.png
  8. BIN
      docs/screenshot.png
  9. 4
      package.json
  10. 5
      src/analytics/Track.js
  11. 4
      src/analytics/segment.js
  12. 2
      src/api/Ledger.js
  13. 12
      src/bridge/RippleJSBridge.js
  14. 4
      src/components/AccountPage/AccountHeaderActions.js
  15. 2
      src/components/AccountPage/index.js
  16. 3
      src/components/BalanceSummary/index.js
  17. 16
      src/components/SelectExchange.js
  18. 3
      src/components/base/Chart/Tooltip.js
  19. 4
      src/components/base/Markdown/index.js
  20. 2
      src/components/layout/Default.js
  21. 2
      src/components/modals/Receive/index.js
  22. 25
      src/components/modals/Send/steps/01-step-amount.js
  23. 6
      src/config/constants.js
  24. 34
      src/helpers/apps/listAppVersions.js
  25. 10
      src/helpers/apps/listApps.js
  26. 10
      src/helpers/apps/listCategories.js
  27. 66
      src/helpers/common.js
  28. 27
      src/helpers/devices/getCurrentFirmware.js
  29. 78
      src/helpers/devices/getLatestFirmwareForDevice.js
  30. 16
      src/helpers/devices/isDashboardOpen.js
  31. 74
      src/helpers/devices/shouldFlashMcu.js
  32. 16
      src/helpers/firmware/getMcus.js
  33. 30
      src/helpers/firmware/getNextMCU.js
  34. 2
      src/helpers/urls.js
  35. BIN
      static/docs/ledgerLogo.png
  36. 11
      static/i18n/en/app.yml
  37. 12
      static/i18n/en/onboarding.yml
  38. 11
      static/i18n/fr/app.yml
  39. 12
      static/i18n/fr/onboarding.yml
  40. 12
      yarn.lock

1
.github/ISSUE_TEMPLATE/bug_report.md

@ -21,3 +21,4 @@ about: Report a bug in Ledger Live Desktop or a regression.
#### Steps to reproduce the behavior #### Steps to reproduce the behavior
<!-- explain steps in detail so we can easily reproduce on our side --> <!-- explain steps in detail so we can easily reproduce on our side -->
<!-- Alternatively provide a screenshot / gif -->

4
.github/ISSUE_TEMPLATE/discussion.md

@ -1,4 +0,0 @@
---
name: 🗣 Start a Discussion
about: Discuss to propose changes or suggest feature requests.
---

18
.github/ISSUE_TEMPLATE/enhancement.md

@ -0,0 +1,18 @@
---
name: 🗣 Start a Discussion
about: Discuss to propose changes to improve the state of Ledger Live.
---
#### Ledger Live Version
<!-- Precise your current app version (Settings > About or bottom-left corner on a crash screen) -->
- Ledger Live **version_here**
#### Part of the application to improve
<!-- which part is to improve? e.g. Send > Step 1 -->
#### Description
<!-- Explain precisely what you think should be improved and how you think it should work -->

14
.github/ISSUE_TEMPLATE/feature_request.md

@ -0,0 +1,14 @@
---
name: ✨ Feature Request
about: Any feature you find missing in Ledger Live? Discuss to suggest feature requests.
---
- [ ] I have checked this feature was not yet requested.
#### Part of the application
<!-- what part of the application would be impacted by this feature? -->
#### Description
<!-- Explain precisely what is the feature about -->

2
.github/ISSUE_TEMPLATE/question.md

@ -4,4 +4,4 @@ about: If you need help using the app, please contact the support at https://sup
--- ---
<!-- Please prefer using the support for app usage questions, Github issues are only for technical / developer usage --> <!-- https://support.ledgerwallet.com/ – Please prefer using the support for app usage questions, Github issues are only for technical / developer usage -->

29
README.md

@ -1,20 +1,17 @@
# Ledger Live - Desktop # Ledger Live (desktop) [![CircleCI](https://circleci.com/gh/LedgerHQ/ledger-live-desktop.svg?style=svg)](https://circleci.com/gh/LedgerHQ/ledger-live-desktop) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/ledger-wallet/localized.svg)](https://crowdin.com/project/ledger-wallet)
[![CircleCI](https://circleci.com/gh/LedgerHQ/ledger-live-desktop.svg?style=svg)](https://circleci.com/gh/LedgerHQ/ledger-live-desktop) > Ledger Live is a new generation wallet desktop application providing a unique interface to maintain multiple cryptocurrencies for your Ledger Nano S / Blue. Manage your device, create accounts, receive and send cryptoassets, [...and many more](https://www.ledgerwallet.com/#LINK_TO_ANNOUNCEMENT).
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/ledger-wallet/localized.svg)](https://crowdin.com/project/ledger-wallet)
:warning: Disclaimer: this project is under active development. Use at your own risks. <p align="center">
<img src="/docs/screenshot.png" width="550"/>
<img src="/static/docs/ledgerLogo.png" width="200"/> </p>
> Ledger Live Desktop is a new generation Ledger Wallet application build with React, Redux and Electron to run natively on the web. The main goal of the app is to provide our users with a single wallet for all crypto currencies supported by our devices. To learn more check out [Ledger](https://www.ledgerwallet.com/?utm_source=redirection&utm_medium=variable)
## Architecture ## Architecture
From one side Ledger Desktop app connected to the Blockchain via the in-house written C++ library - LibCore and from the other it communicates to the Ledger Hardware Device to securely sign all transactions. Ledger Live is an hybrid desktop application built with Electron, React, Redux, RxJS,.. and highly optimized with [ledger-core](https://github.com/LedgerHQ/lib-ledger-core) C++ library to deal with blockchains (sync, broadcast,..) via [ledger-core-node-bindings](https://github.com/LedgerHQ/lib-ledger-core-node-bindings). It communicates to Ledger hardware wallet devices (Nano S / Blue) to verify address and sign transactions with [ledgerjs](https://github.com/LedgerHQ/ledgerjs). Some logic is shared with [live-common](https://github.com/LedgerHQ/ledger-live-common).
<p align="center"> <p align="center">
<img src="/static/docs/architecture.png" width="550"/> <img src="/docs/architecture.png" width="550"/>
</p> </p>
## Setup ## Setup
@ -32,23 +29,15 @@ From one side Ledger Desktop app connected to the Blockchain via the in-house wr
## Install ## Install
1. Clone or fork the repo
```bash
git clone git@github.com:LedgerHQ/ledger-live-desktop.git
```
2. Install dependencies
```bash ```bash
# install dependencies
yarn yarn
``` ```
## Run ## Run
Launch the app
```bash ```bash
# launch the app
yarn start yarn start
``` ```

0
static/docs/architecture.png → docs/architecture.png

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

4
package.json

@ -40,8 +40,8 @@
"@ledgerhq/hw-app-xrp": "^4.13.0", "@ledgerhq/hw-app-xrp": "^4.13.0",
"@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport": "^4.13.0",
"@ledgerhq/hw-transport-node-hid": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "^4.13.0",
"@ledgerhq/ledger-core": "2.0.0-rc.3", "@ledgerhq/ledger-core": "2.0.0-rc.4",
"@ledgerhq/live-common": "2.31.0", "@ledgerhq/live-common": "^2.32.0",
"animated": "^0.2.2", "animated": "^0.2.2",
"async": "^2.6.1", "async": "^2.6.1",
"axios": "^0.18.0", "axios": "^0.18.0",

5
src/analytics/Track.js

@ -7,6 +7,7 @@ class Track extends PureComponent<{
onUnmount?: boolean, onUnmount?: boolean,
onUpdate?: boolean, onUpdate?: boolean,
event: string, event: string,
mandatory?: boolean,
}> { }> {
componentDidMount() { componentDidMount() {
if (typeof this.props.event !== 'string') { if (typeof this.props.event !== 'string') {
@ -21,8 +22,8 @@ class Track extends PureComponent<{
if (this.props.onUnmount) this.track() if (this.props.onUnmount) this.track()
} }
track = () => { track = () => {
const { event, onMount, onUnmount, onUpdate, ...properties } = this.props const { event, onMount, onUnmount, onUpdate, mandatory, ...properties } = this.props
track(event, properties) track(event, properties, mandatory)
} }
render() { render() {
return null return null

4
src/analytics/segment.js

@ -72,9 +72,9 @@ export const stop = () => {
analytics.reset() analytics.reset()
} }
export const track = (event: string, properties: ?Object) => { export const track = (event: string, properties: ?Object, mandatory: ?boolean) => {
logger.analyticsTrack(event, properties) logger.analyticsTrack(event, properties)
if (!storeInstance || !shareAnalyticsSelector(storeInstance.getState())) { if (!storeInstance || (!mandatory && !shareAnalyticsSelector(storeInstance.getState()))) {
return return
} }
const { analytics } = window const { analytics } = window

2
src/api/Ledger.js

@ -3,4 +3,4 @@ import type { Currency } from '@ledgerhq/live-common/lib/types'
import { LEDGER_REST_API_BASE } from 'config/constants' import { LEDGER_REST_API_BASE } from 'config/constants'
export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string => export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string =>
ledgerExplorerId ? `${LEDGER_REST_API_BASE}blockchain/v2/${ledgerExplorerId}` : null ledgerExplorerId ? `${LEDGER_REST_API_BASE}/blockchain/v2/${ledgerExplorerId}` : null

12
src/bridge/RippleJSBridge.js

@ -351,7 +351,12 @@ const RippleJSBridge: WalletBridge<Transaction> = {
return unsubscribe return unsubscribe
}), }),
synchronize: ({ endpointConfig, freshAddress, blockHeight }) => synchronize: ({
endpointConfig,
freshAddress,
blockHeight,
operations: { length: currentOpsLength },
}) =>
Observable.create(o => { Observable.create(o => {
let finished = false let finished = false
const unsubscribe = () => { const unsubscribe = () => {
@ -394,7 +399,10 @@ const RippleJSBridge: WalletBridge<Transaction> = {
o.next(a => ({ ...a, balance })) o.next(a => ({ ...a, balance }))
const transactions = await api.getTransactions(freshAddress, { const transactions = await api.getTransactions(freshAddress, {
minLedgerVersion: Math.max(blockHeight, minLedgerVersion), minLedgerVersion: Math.max(
currentOpsLength === 0 ? 0 : blockHeight, // if there is no ops, it might be after a clear and we prefer to pull from the oldest possible history
minLedgerVersion,
),
maxLedgerVersion, maxLedgerVersion,
}) })

4
src/components/AccountPage/AccountHeaderActions.js

@ -62,7 +62,7 @@ class AccountHeaderActions extends PureComponent<Props> {
const { account, openModal, t } = this.props const { account, openModal, t } = this.props
return ( return (
<Box horizontal alignItems="center" justifyContent="flex-end" flow={2}> <Box horizontal alignItems="center" justifyContent="flex-end" flow={2}>
{account.operations.length > 0 && ( {account.operations.length > 0 || account.balance > 0 ? (
<Fragment> <Fragment>
<Button small primary onClick={() => openModal(MODAL_SEND, { account })}> <Button small primary onClick={() => openModal(MODAL_SEND, { account })}>
<Box horizontal flow={1} alignItems="center"> <Box horizontal flow={1} alignItems="center">
@ -78,7 +78,7 @@ class AccountHeaderActions extends PureComponent<Props> {
</Box> </Box>
</Button> </Button>
</Fragment> </Fragment>
)} ) : null}
<Tooltip render={() => t('app:account.settings.title')}> <Tooltip render={() => t('app:account.settings.title')}>
<ButtonSettings onClick={() => openModal(MODAL_SETTINGS_ACCOUNT, { account })}> <ButtonSettings onClick={() => openModal(MODAL_SETTINGS_ACCOUNT, { account })}>
<Box justifyContent="center"> <Box justifyContent="center">

2
src/components/AccountPage/index.js

@ -82,7 +82,7 @@ class AccountPage extends PureComponent<Props> {
<AccountHeaderActions account={account} /> <AccountHeaderActions account={account} />
</Box> </Box>
{account.operations.length > 0 ? ( {account.operations.length > 0 || account.balance > 0 ? (
<Fragment> <Fragment>
<Box mb={7}> <Box mb={7}>
<BalanceSummary <BalanceSummary

3
src/components/BalanceSummary/index.js

@ -1,6 +1,7 @@
// @flow // @flow
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import moment from 'moment'
import { formatShort } from '@ledgerhq/live-common/lib/helpers/currencies' import { formatShort } from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Currency, Account } from '@ledgerhq/live-common/lib/types' import type { Currency, Account } from '@ledgerhq/live-common/lib/types'
@ -90,7 +91,7 @@ const BalanceSummary = ({
val={d.value} val={d.value}
/> />
<Box ff="Open Sans|Regular" color="grey" fontSize={3} mt={2}> <Box ff="Open Sans|Regular" color="grey" fontSize={3} mt={2}>
{d.date.toISOString().substr(0, 10)} {moment(d.date).format('LL')}
</Box> </Box>
</Fragment> </Fragment>
) )

16
src/components/SelectExchange.js

@ -37,12 +37,14 @@ class SelectExchange extends Component<
prevFromTo: string, prevFromTo: string,
exchanges: ?(Exchange[]), exchanges: ?(Exchange[]),
error: ?Error, error: ?Error,
isLoading: boolean,
}, },
> { > {
state = { state = {
prevFromTo: '', // eslint-disable-line prevFromTo: '', // eslint-disable-line
exchanges: null, exchanges: null,
error: null, error: null,
isLoading: false,
} }
static getDerivedStateFromProps(nextProps: *, prevState: *) { static getDerivedStateFromProps(nextProps: *, prevState: *) {
@ -75,25 +77,25 @@ class SelectExchange extends Component<
async _load() { async _load() {
this._loadId++ this._loadId++
if (this._unmounted) return if (this._unmounted) return
this.setState({ exchanges: [] }) this.setState({ exchanges: [], isLoading: true })
const { _loadId } = this const { _loadId } = this
const { from, to } = this.props const { from, to } = this.props
try { try {
const exchanges = await getExchanges(from, to) const exchanges = await getExchanges(from, to)
if (!this._unmounted && this._loadId === _loadId) { if (!this._unmounted && this._loadId === _loadId) {
this.setState({ exchanges }) this.setState({ exchanges, isLoading: false })
} }
} catch (error) { } catch (error) {
logger.error(error) logger.error(error)
if (!this._unmounted && this._loadId === _loadId) { if (!this._unmounted && this._loadId === _loadId) {
this.setState({ error }) this.setState({ error, isLoading: false })
} }
} }
} }
render() { render() {
const { onChange, exchangeId, style, t, from, to, ...props } = this.props const { onChange, exchangeId, style, t, from, to, ...props } = this.props
const { exchanges, error } = this.state const { exchanges, error, isLoading } = this.state
const options = exchanges ? exchanges.map(e => ({ value: e.id, label: e.name, ...e })) : [] const options = exchanges ? exchanges.map(e => ({ value: e.id, label: e.name, ...e })) : []
const value = options.find(e => e.id === exchangeId) const value = options.find(e => e.id === exchangeId)
@ -115,10 +117,12 @@ class SelectExchange extends Component<
value={value} value={value}
options={options} options={options}
onChange={onChange} onChange={onChange}
isLoading={options.length === 0} isLoading={isLoading}
placeholder={t('app:common.selectExchange')} placeholder={t('app:common.selectExchange')}
noOptionsMessage={({ inputValue }) => noOptionsMessage={({ inputValue }) =>
t('app:common.selectExchangeNoOption', { exchangeName: inputValue }) inputValue
? t('app:common.selectExchangeNoOption', { exchangeName: inputValue })
: t('app:common.selectExchangeNoOptionAtAll')
} }
{...props} {...props}
/> />

3
src/components/base/Chart/Tooltip.js

@ -1,6 +1,7 @@
// @flow // @flow
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import moment from 'moment'
import styled from 'styled-components' import styled from 'styled-components'
import type { Unit, Currency } from '@ledgerhq/live-common/lib/types' import type { Unit, Currency } from '@ledgerhq/live-common/lib/types'
@ -68,7 +69,7 @@ const Tooltip = ({
/> />
)} )}
<Box ff="Open Sans|Regular" color="grey" fontSize={3} mt={2}> <Box ff="Open Sans|Regular" color="grey" fontSize={3} mt={2}>
{item.date.toISOString().substr(0, 10)} {moment(item.date).format('LL')}
</Box> </Box>
</Fragment> </Fragment>
)} )}

4
src/components/base/Markdown/index.js

@ -79,6 +79,10 @@ export const Notes = styled(Box).attrs({
color: #6a737d; color: #6a737d;
} }
strong {
font-weight: bold;
}
img { img {
max-width: 100%; max-width: 100%;
} }

2
src/components/layout/Default.js

@ -13,6 +13,7 @@ import type { Location } from 'react-router'
import * as modals from 'components/modals' import * as modals from 'components/modals'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll' import GrowScroll from 'components/base/GrowScroll'
import Track from 'analytics/Track'
import AccountPage from 'components/AccountPage' import AccountPage from 'components/AccountPage'
import DashboardPage from 'components/DashboardPage' import DashboardPage from 'components/DashboardPage'
@ -84,6 +85,7 @@ class Default extends Component<Props> {
<TriggerAppReady /> <TriggerAppReady />
{process.platform === 'darwin' && <AppRegionDrag />} {process.platform === 'darwin' && <AppRegionDrag />}
<ExportLogsBtn hookToShortcut /> <ExportLogsBtn hookToShortcut />
<Track mandatory onMount event="App Starts" />
<OnboardingOrElse> <OnboardingOrElse>
<IsUnlocked> <IsUnlocked>

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

@ -194,7 +194,7 @@ class ReceiveModal extends PureComponent<Props, State> {
? [verifyAddressError.name === 'UserRefusedAddress' ? 2 : 3] ? [verifyAddressError.name === 'UserRefusedAddress' ? 2 : 3]
: [] : []
const isModalLocked = stepId === 'confirm' && isAddressVerified === null const isModalLocked = stepId === 'receive' && isAddressVerified === null
return ( return (
<Modal <Modal

25
src/components/modals/Send/steps/01-step-amount.js

@ -2,6 +2,7 @@
import React, { PureComponent, Fragment } from 'react' import React, { PureComponent, Fragment } from 'react'
import logger from 'logger'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Label from 'components/base/Label' import Label from 'components/base/Label'
@ -92,14 +93,14 @@ export class StepAmountFooter extends PureComponent<
StepProps<*>, StepProps<*>,
{ {
totalSpent: number, totalSpent: number,
canBeSpent: boolean, canNext: boolean,
isSyncing: boolean, isSyncing: boolean,
}, },
> { > {
state = { state = {
isSyncing: false, isSyncing: false,
totalSpent: 0, totalSpent: 0,
canBeSpent: true, canNext: false,
} }
componentDidMount() { componentDidMount() {
@ -127,6 +128,7 @@ export class StepAmountFooter extends PureComponent<
const syncId = ++this.syncId const syncId = ++this.syncId
if (!account || !transaction || !bridge) { if (!account || !transaction || !bridge) {
this.setState({ canNext: false, isSyncing: false })
return return
} }
@ -135,21 +137,26 @@ export class StepAmountFooter extends PureComponent<
try { try {
const totalSpent = await bridge.getTotalSpent(account, transaction) const totalSpent = await bridge.getTotalSpent(account, transaction)
if (syncId !== this.syncId) return if (syncId !== this.syncId) return
const isRecipientValid = await bridge.isRecipientValid(
account.currency,
bridge.getTransactionRecipient(account, transaction),
)
if (syncId !== this.syncId) return
const canBeSpent = await bridge const canBeSpent = await bridge
.checkCanBeSpent(account, transaction) .checkCanBeSpent(account, transaction)
.then(() => true, () => false) .then(() => true, () => false)
if (syncId !== this.syncId) return if (syncId !== this.syncId) return
this.setState({ totalSpent, canBeSpent, isSyncing: false }) const canNext = isRecipientValid && canBeSpent
this.setState({ totalSpent, canNext, isSyncing: false })
} catch (err) { } catch (err) {
this.setState({ isSyncing: false }) logger.critical(err)
this.setState({ totalSpent: 0, canNext: false, isSyncing: false })
} }
} }
render() { render() {
const { t, transitionTo, account, transaction, bridge } = this.props const { t, transitionTo, account } = this.props
const { totalSpent, canBeSpent, isSyncing } = this.state const { isSyncing, totalSpent, canNext } = this.state
const canNext =
account && transaction && bridge && bridge.isValidTransaction(account, transaction)
return ( return (
<Fragment> <Fragment>
<Box grow> <Box grow>
@ -186,7 +193,7 @@ export class StepAmountFooter extends PureComponent<
{isSyncing && <Spinner size={10} />} {isSyncing && <Spinner size={10} />}
</Box> </Box>
</Box> </Box>
<Button disabled={!canBeSpent || !canNext} primary onClick={() => transitionTo('device')}> <Button disabled={!canNext} primary onClick={() => transitionTo('device')}>
{t('app:common.continue')} {t('app:common.continue')}
</Button> </Button>
</Fragment> </Fragment>

6
src/config/constants.js

@ -43,15 +43,15 @@ export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 30 * 1000)
export const LEDGER_COUNTERVALUES_API = stringFromEnv( export const LEDGER_COUNTERVALUES_API = stringFromEnv(
'LEDGER_COUNTERVALUES_API', 'LEDGER_COUNTERVALUES_API',
'https://beta.manager.live.ledger.fr/countervalues', 'https://countervalues.api.live.ledger.com',
) )
export const LEDGER_REST_API_BASE = stringFromEnv( export const LEDGER_REST_API_BASE = stringFromEnv(
'LEDGER_REST_API_BASE', 'LEDGER_REST_API_BASE',
'https://explorers.api.live.ledger.com/', 'https://explorers.api.live.ledger.com',
) )
export const MANAGER_API_BASE = stringFromEnv( export const MANAGER_API_BASE = stringFromEnv(
'MANAGER_API_BASE', 'MANAGER_API_BASE',
'https://beta.manager.live.ledger.fr/api', 'https://manager.api.live.ledger.com/api',
) )
export const BASE_SOCKET_URL = stringFromEnv('BASE_SOCKET_URL', 'wss://api.ledgerwallet.com/update') export const BASE_SOCKET_URL = stringFromEnv('BASE_SOCKET_URL', 'wss://api.ledgerwallet.com/update')

34
src/helpers/apps/listAppVersions.js

@ -7,25 +7,19 @@ import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware' import getCurrentFirmware from 'helpers/devices/getCurrentFirmware'
export default async (deviceInfo: DeviceInfo) => { export default async (deviceInfo: DeviceInfo) => {
try { const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) const firmwareData = await getCurrentFirmware({
const firmwareData = await getCurrentFirmware({ deviceId: deviceData.id,
deviceId: deviceData.id, fullVersion: deviceInfo.fullVersion,
fullVersion: deviceInfo.fullVersion, provider: deviceInfo.providerId,
provider: deviceInfo.providerId, })
}) const params = {
const params = { provider: deviceInfo.providerId,
provider: deviceInfo.providerId, current_se_firmware_final_version: firmwareData.id,
current_se_firmware_final_version: firmwareData.id, device_version: deviceData.id,
device_version: deviceData.id,
}
const {
data: { application_versions },
} = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
return application_versions.length > 0 ? application_versions : []
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw err
} }
const {
data: { application_versions },
} = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
return application_versions.length > 0 ? application_versions : []
} }

10
src/helpers/apps/listApps.js

@ -4,12 +4,6 @@ import network from 'api/network'
import { GET_APPLICATIONS } from 'helpers/urls' import { GET_APPLICATIONS } from 'helpers/urls'
export default async () => { export default async () => {
try { const { data } = await network({ method: 'GET', url: GET_APPLICATIONS })
const { data } = await network({ method: 'GET', url: GET_APPLICATIONS }) return data.length > 0 ? data : []
return data.length > 0 ? data : []
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw err
}
} }

10
src/helpers/apps/listCategories.js

@ -4,12 +4,6 @@ import network from 'api/network'
import { GET_CATEGORIES } from 'helpers/urls' import { GET_CATEGORIES } from 'helpers/urls'
export default async () => { export default async () => {
try { const { data } = await network({ method: 'GET', url: GET_CATEGORIES })
const { data } = await network({ method: 'GET', url: GET_CATEGORIES }) return data.length > 0 ? data : []
return data.length > 0 ? data : []
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw err
}
} }

66
src/helpers/common.js

@ -27,44 +27,38 @@ export type LedgerScriptParams = {
* Retrieve targetId and firmware version from device * Retrieve targetId and firmware version from device
*/ */
export async function getFirmwareInfo(transport: Transport<*>) { export async function getFirmwareInfo(transport: Transport<*>) {
try { const res = await transport.send(...APDUS.GET_FIRMWARE)
const res = await transport.send(...APDUS.GET_FIRMWARE) const byteArray = [...res]
const byteArray = [...res] const data = byteArray.slice(0, byteArray.length - 2)
const data = byteArray.slice(0, byteArray.length - 2) const targetIdStr = Buffer.from(data.slice(0, 4))
const targetIdStr = Buffer.from(data.slice(0, 4)) const targetId = targetIdStr.readUIntBE(0, 4)
const targetId = targetIdStr.readUIntBE(0, 4) const seVersionLength = data[4]
const seVersionLength = data[4] const seVersion = Buffer.from(data.slice(5, 5 + seVersionLength)).toString()
const seVersion = Buffer.from(data.slice(5, 5 + seVersionLength)).toString() const flagsLength = data[5 + seVersionLength]
const flagsLength = data[5 + seVersionLength] const flags = Buffer.from(
const flags = Buffer.from( data.slice(5 + seVersionLength + 1, 5 + seVersionLength + 1 + flagsLength),
data.slice(5 + seVersionLength + 1, 5 + seVersionLength + 1 + flagsLength), ).toString()
).toString()
const mcuVersionLength = data[5 + seVersionLength + 1 + flagsLength] const mcuVersionLength = data[5 + seVersionLength + 1 + flagsLength]
let mcuVersion = Buffer.from( let mcuVersion = Buffer.from(
data.slice( data.slice(
7 + seVersionLength + flagsLength, 7 + seVersionLength + flagsLength,
7 + seVersionLength + flagsLength + mcuVersionLength, 7 + seVersionLength + flagsLength + mcuVersionLength,
), ),
) )
if (mcuVersion[mcuVersion.length - 1] === 0) { if (mcuVersion[mcuVersion.length - 1] === 0) {
mcuVersion = mcuVersion.slice(0, mcuVersion.length - 1) mcuVersion = mcuVersion.slice(0, mcuVersion.length - 1)
} }
mcuVersion = mcuVersion.toString() mcuVersion = mcuVersion.toString()
if (!seVersionLength) { if (!seVersionLength) {
return { return {
targetId, targetId,
seVersion: '0.0.0', seVersion: '0.0.0',
flags: '', flags: '',
mcuVersion: '', mcuVersion: '',
}
} }
return { targetId, seVersion, flags, mcuVersion }
} catch (err) {
const error = new Error(err.message)
error.stack = err.stack
throw error
} }
return { targetId, seVersion, flags, mcuVersion }
} }

27
src/helpers/devices/getCurrentFirmware.js

@ -9,22 +9,15 @@ type Input = {
provider: number, provider: number,
} }
let error
export default async (input: Input): Promise<*> => { export default async (input: Input): Promise<*> => {
try { const { data } = await network({
const { data } = await network({ method: 'POST',
method: 'POST', url: GET_CURRENT_FIRMWARE,
url: GET_CURRENT_FIRMWARE, data: {
data: { device_version: input.deviceId,
device_version: input.deviceId, version_name: input.fullVersion,
version_name: input.fullVersion, provider: input.provider,
provider: input.provider, },
}, })
}) return data
return data
} catch (err) {
error = Error(err.message)
error.stack = err.stack
throw error
}
} }

78
src/helpers/devices/getLatestFirmwareForDevice.js

@ -10,53 +10,47 @@ import getCurrentFirmware from './getCurrentFirmware'
import getDeviceVersion from './getDeviceVersion' import getDeviceVersion from './getDeviceVersion'
export default async (deviceInfo: DeviceInfo) => { export default async (deviceInfo: DeviceInfo) => {
try { // Get device infos from targetId
// Get device infos from targetId const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
// Get firmware infos with firmware name and device version
// Get firmware infos with firmware name and device version const seFirmwareVersion = await getCurrentFirmware({
const seFirmwareVersion = await getCurrentFirmware({ fullVersion: deviceInfo.fullVersion,
fullVersion: deviceInfo.fullVersion, deviceId: deviceVersion.id,
deviceId: deviceVersion.id, provider: deviceInfo.providerId,
})
// Fetch next possible firmware
const { data } = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
current_se_firmware_final_version: seFirmwareVersion.id,
device_version: deviceVersion.id,
provider: deviceInfo.providerId, provider: deviceInfo.providerId,
}) },
})
// Fetch next possible firmware
const { data } = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
current_se_firmware_final_version: seFirmwareVersion.id,
device_version: deviceVersion.id,
provider: deviceInfo.providerId,
},
})
if (data.result === 'null') {
return null
}
const { se_firmware_osu_version } = data if (data.result === 'null') {
const { next_se_firmware_final_version } = se_firmware_osu_version return null
const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version) }
const mcus = await getMcus() 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 currentMcuVersionId = mcus const mcus = await getMcus()
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)
if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) { const currentMcuVersionId = mcus
return { .filter(mcu => mcu.name === deviceInfo.mcuVersion)
...se_firmware_osu_version, .map(mcu => mcu.id)
shouldFlashMcu: true,
}
}
return { ...se_firmware_osu_version, shouldFlashMcu: false } if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) {
} catch (err) { return {
const error = Error(err.message) ...se_firmware_osu_version,
error.stack = err.stack shouldFlashMcu: true,
throw error }
} }
return { ...se_firmware_osu_version, shouldFlashMcu: false }
} }

16
src/helpers/devices/isDashboardOpen.js

@ -7,16 +7,10 @@ import { getFirmwareInfo } from 'helpers/common'
type Result = boolean type Result = boolean
export default async (transport: Transport<*>): Promise<Result> => { export default async (transport: Transport<*>): Promise<Result> => {
try { const { targetId, seVersion } = await getFirmwareInfo(transport)
const { targetId, seVersion } = await getFirmwareInfo(transport) if (targetId && seVersion) {
if (targetId && seVersion) { return true
return true
}
return false
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw error
} }
return false
} }

74
src/helpers/devices/shouldFlashMcu.js

@ -10,46 +10,40 @@ import getOsuFirmware from './getOsuFirmware'
import getDeviceVersion from './getDeviceVersion' import getDeviceVersion from './getDeviceVersion'
export default async (deviceInfo: DeviceInfo): Promise<boolean> => { export default async (deviceInfo: DeviceInfo): Promise<boolean> => {
try { // Get device infos from targetId
// Get device infos from targetId const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
// Get firmware infos with firmware name and device version
// Get firmware infos with firmware name and device version const seFirmwareVersion = await getOsuFirmware({
const seFirmwareVersion = await getOsuFirmware({ version: deviceInfo.fullVersion,
version: deviceInfo.fullVersion, deviceId: deviceVersion.id,
deviceId: deviceVersion.id, provider: deviceInfo.providerId,
})
// Fetch next possible firmware
const { data } = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
current_se_firmware_final_version: seFirmwareVersion.id,
device_version: deviceVersion.id,
provider: deviceInfo.providerId, provider: deviceInfo.providerId,
}) },
})
// Fetch next possible firmware
const { data } = await network({ if (data.result === 'null') {
method: 'POST', return false
url: GET_LATEST_FIRMWARE,
data: {
current_se_firmware_final_version: seFirmwareVersion.id,
device_version: deviceVersion.id,
provider: deviceInfo.providerId,
},
})
if (data.result === 'null') {
return false
}
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 mcus = await getMcus()
const currentMcuVersionId = mcus
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)
return !seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw error
} }
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 mcus = await getMcus()
const currentMcuVersionId = mcus
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)
return !seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)
} }

16
src/helpers/firmware/getMcus.js

@ -4,16 +4,10 @@ import network from 'api/network'
import { GET_MCUS } from 'helpers/urls' import { GET_MCUS } from 'helpers/urls'
export default async (): Promise<*> => { export default async (): Promise<*> => {
try { const { data } = await network({
const { data } = await network({ method: 'GET',
method: 'GET', url: GET_MCUS,
url: GET_MCUS, })
})
return data return data
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw err
}
} }

30
src/helpers/firmware/getNextMCU.js

@ -7,24 +7,18 @@ import { createCustomErrorClass } from 'helpers/errors'
const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError') const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')
export default async (bootloaderVersion: string): Promise<*> => { export default async (bootloaderVersion: string): Promise<*> => {
try { const { data } = await network({
const { data } = await network({ method: 'POST',
method: 'POST', url: GET_NEXT_MCU,
url: GET_NEXT_MCU, data: {
data: { bootloader_version: bootloaderVersion,
bootloader_version: bootloaderVersion, },
}, })
})
// FIXME: nextVersion will not be able to "default" when // FIXME: nextVersion will not be able to "default" when
// Error handling is standardize on the API side // Error handling is standardize on the API side
if (data === 'default' || !data.name) { if (data === 'default' || !data.name) {
throw new LatestMCUInstalledError('there is no next mcu version to install') throw new LatestMCUInstalledError('there is no next mcu version to install')
}
return data
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw err
} }
return data
} }

2
src/helpers/urls.js

@ -14,6 +14,8 @@ const wsURLBuilder = (endpoint: string) => (params?: Object) =>
// const wsURLBuilderProxy = (endpoint: string) => (params?: Object) => // const wsURLBuilderProxy = (endpoint: string) => (params?: Object) =>
// `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}` // `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}`
// FIXME we shouldn't do this here. we should just collocate these where it's used.
export const GET_FINAL_FIRMWARE: string = managerUrlbuilder('firmware_final_versions') export const GET_FINAL_FIRMWARE: string = managerUrlbuilder('firmware_final_versions')
export const GET_DEVICE_VERSION: string = managerUrlbuilder('get_device_version') export const GET_DEVICE_VERSION: string = managerUrlbuilder('get_device_version')
export const APPLICATIONS_BY_DEVICE: string = managerUrlbuilder('get_apps') export const APPLICATIONS_BY_DEVICE: string = managerUrlbuilder('get_apps')

BIN
static/docs/ledgerLogo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

11
static/i18n/en/app.yml

@ -7,7 +7,7 @@ common:
cancel: Cancel cancel: Cancel
delete: Delete delete: Delete
continue: Continue continue: Continue
learnMore: Learn More learnMore: Learn more
skipThisStep: Skip this step skipThisStep: Skip this step
needHelp: Need help? needHelp: Need help?
chooseWalletPlaceholder: Choose a wallet... chooseWalletPlaceholder: Choose a wallet...
@ -18,6 +18,7 @@ common:
selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"' selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"'
selectExchange: Select an exchange selectExchange: Select an exchange
selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"' selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"'
selectExchangeNoOptionAtAll: 'No exchange found'
sortBy: Sort by sortBy: Sort by
search: Search search: Search
save: Save save: Save
@ -137,7 +138,7 @@ deviceConnect:
dashboard: Not used. # This key is not used. Still managed in JS. dashboard: Not used. # This key is not used. Still managed in JS.
emptyState: emptyState:
sidebar: sidebar:
text: Press the + button to add an account to your portfolio text: Press this button to add accounts to your portfolio
dashboard: dashboard:
title: 'Add accounts to your portfolio' title: 'Add accounts to your portfolio'
desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio. desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio.
@ -273,7 +274,7 @@ send:
totalSpent: Total to debit totalSpent: Total to debit
steps: steps:
amount: amount:
title: crypto assets title: Details
selectAccountDebit: Select an account to debit selectAccountDebit: Select an account to debit
recipientAddress: Recipient address # can't control the tooltip! recipientAddress: Recipient address # can't control the tooltip!
amount: Amount amount: Amount
@ -360,7 +361,7 @@ settings: # Always ensure descriptions carry full stops (.)
reportErrors: Report bugs reportErrors: Report bugs
reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features. reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features.
about: about:
desc: Learn about Ledger Live features desc: Information about Ledger Live, terms and conditions, and privacy policy.
help: help:
desc: Learn about Ledger Live features or get help. desc: Learn about Ledger Live features or get help.
version: Version version: Version
@ -370,7 +371,7 @@ settings: # Always ensure descriptions carry full stops (.)
terms: Terms and conditions terms: Terms and conditions
termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions. termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions.
privacy: Privacy policy privacy: Privacy policy
privacyDesc: Refer to our privacy policy to learn what personal data we collect, and why and how we use it. privacyDesc: Refer to our privacy policy to learn what personal data we collect, why and how we use it.
hardResetModal: hardResetModal:
title: Reset Ledger Live title: Reset Ledger Live
desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet. desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet.

12
static/i18n/en/onboarding.yml

@ -132,19 +132,19 @@ analytics:
title: Share analytics title: Share analytics
desc: Enable analytics to help Ledger understand how to improve the user experience. desc: Enable analytics to help Ledger understand how to improve the user experience.
mandatoryContextual: mandatoryContextual:
item1: Page visits item1: In-app page visits
item2: Actions (send, receive, logout) item2: Actions (send, receive, lock)
item3: Clicks item3: Clicks
item4: Redirections to webpages item4: Redirections to webpages
item5: Scrolled to end of page item5: Scrolled to end of page
item6: Install/Uninstall item6: Install/Uninstall
item7: Number of accounts, currencies and operations item7: Number of accounts, currencies and operations
item8: Overall and page session duration item8: Overall and page session duration
item9: Device product ID item9: Type of Ledger device
item10: Device firmware and app versions item10: Device firmware and app version numbers
sentryLogs: sentryLogs:
title: Report bugs title: Report bugs
desc: Automatically send reports to help Ledger fix bugs desc: Automatically send reports to help Ledger fix bugs.
technicalData: technicalData:
title: Technical data * title: Technical data *
desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data. desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data.
@ -155,7 +155,7 @@ analytics:
item2: OS name and version item2: OS name and version
item3: Ledger Live version item3: Ledger Live version
item4: Application language or region item4: Application language or region
item5: OS Language/Region item5: OS language or region
finish: finish:
title: Your device is ready! title: Your device is ready!
desc: Proceed to your portfolio and start adding your accounts... desc: Proceed to your portfolio and start adding your accounts...

11
static/i18n/fr/app.yml

@ -8,7 +8,7 @@ common:
cancel: Cancel cancel: Cancel
delete: Delete delete: Delete
continue: Continue continue: Continue
learnMore: Learn More learnMore: Learn more
skipThisStep: Skip this step skipThisStep: Skip this step
needHelp: Need help? needHelp: Need help?
chooseWalletPlaceholder: Choose a wallet... chooseWalletPlaceholder: Choose a wallet...
@ -19,6 +19,7 @@ common:
selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"' selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"'
selectExchange: Select an exchange selectExchange: Select an exchange
selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"' selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"'
selectExchangeNoOptionAtAll: 'No exchange found'
sortBy: Sort by sortBy: Sort by
search: Search search: Search
save: Save save: Save
@ -134,7 +135,7 @@ deviceConnect:
dashboard: Not used. dashboard: Not used.
emptyState: emptyState:
sidebar: sidebar:
text: Press the + button to add an account to your portfolio text: Press this button to add accounts to your portfolio
dashboard: dashboard:
title: 'Add accounts to your portfolio' title: 'Add accounts to your portfolio'
desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio. desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio.
@ -270,7 +271,7 @@ send:
totalSpent: Total to debit totalSpent: Total to debit
steps: steps:
amount: amount:
title: crypto assets title: Details
selectAccountDebit: Select an account to debit selectAccountDebit: Select an account to debit
recipientAddress: Recipient address recipientAddress: Recipient address
amount: Amount amount: Amount
@ -357,7 +358,7 @@ settings:
reportErrors: Report bugs reportErrors: Report bugs
reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features. reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features.
about: about:
desc: Learn about Ledger Live features desc: Information about Ledger Live, terms and conditions, and privacy policy.
help: help:
desc: Learn about Ledger Live features or get help. desc: Learn about Ledger Live features or get help.
version: Version version: Version
@ -367,7 +368,7 @@ settings:
terms: Terms and conditions terms: Terms and conditions
termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions. termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions.
privacy: Privacy policy privacy: Privacy policy
privacyDesc: Refer to our privacy policy to learn what personal data we collect, and why and how we use it. privacyDesc: Refer to our privacy policy to learn what personal data we collect, why and how we use it.
hardResetModal: hardResetModal:
title: Reset Ledger Live title: Reset Ledger Live
desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet. desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet.

12
static/i18n/fr/onboarding.yml

@ -133,19 +133,19 @@ analytics:
title: Share analytics title: Share analytics
desc: Enable analytics to help Ledger understand how to improve the user experience. desc: Enable analytics to help Ledger understand how to improve the user experience.
mandatoryContextual: mandatoryContextual:
item1: Page visits item1: In-app page visits
item2: Actions (send, receive, logout) item2: Actions (send, receive, lock)
item3: Clicks item3: Clicks
item4: Redirections to webpages item4: Redirections to webpages
item5: Scrolled to end of page item5: Scrolled to end of page
item6: Install/Uninstall item6: Install/Uninstall
item7: Number of accounts, currencies and operations item7: Number of accounts, currencies and operations
item8: Overall and page session duration item8: Overall and page session duration
item9: Device product ID item9: Type of Ledger device
item10: Device firmware and app versions item10: Device firmware and app version numbers
sentryLogs: sentryLogs:
title: Report bugs title: Report bugs
desc: Automatically send reports to help Ledger fix bugs desc: Automatically send reports to help Ledger fix bugs.
technicalData: technicalData:
title: Technical data * title: Technical data *
desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data. desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data.
@ -156,7 +156,7 @@ analytics:
item2: OS name and version item2: OS name and version
item3: Ledger Live version item3: Ledger Live version
item4: Application language or region item4: Application language or region
item5: OS Language/Region item5: OS language or region
finish: finish:
title: Your device is ready! title: Your device is ready!
desc: Proceed to your portfolio and start adding your accounts... desc: Proceed to your portfolio and start adding your accounts...

12
yarn.lock

@ -1521,9 +1521,9 @@
dependencies: dependencies:
events "^2.0.0" events "^2.0.0"
"@ledgerhq/ledger-core@2.0.0-rc.3": "@ledgerhq/ledger-core@2.0.0-rc.4":
version "2.0.0-rc.3" version "2.0.0-rc.4"
resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.3.tgz#21b04239e9ba6b7fdcb89958eea8ad47a4a28a88" resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.4.tgz#0ec80a763c666658bea94bd38b86aa90d5a24906"
dependencies: dependencies:
"@ledgerhq/hw-app-btc" "^4.7.3" "@ledgerhq/hw-app-btc" "^4.7.3"
"@ledgerhq/hw-transport-node-hid" "^4.7.6" "@ledgerhq/hw-transport-node-hid" "^4.7.6"
@ -1534,9 +1534,9 @@
npm "^5.7.1" npm "^5.7.1"
prebuild-install "^2.2.2" prebuild-install "^2.2.2"
"@ledgerhq/live-common@2.31.0": "@ledgerhq/live-common@^2.32.0":
version "2.31.0" version "2.32.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.31.0.tgz#0f599a1e23b64d9ed74a845d3a9c82f0696f1df3" resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.32.0.tgz#6c2108d58ec44335077d87442a6418e0ec4d3372"
dependencies: dependencies:
axios "^0.18.0" axios "^0.18.0"
invariant "^2.2.2" invariant "^2.2.2"

Loading…
Cancel
Save