Browse Source

Merge pull request #258 from meriadec/common-countervalues

Use wallet-common countervalues helpers
master
Loëck Vézien 7 years ago
committed by GitHub
parent
commit
acdc045393
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      package.json
  2. 77
      src/actions/counterValues.js
  3. 4
      src/components/AccountPage/index.js
  4. 40
      src/components/CounterValue/index.js
  5. 18
      src/components/CounterValue/stories.js
  6. 4
      src/components/DashboardPage/index.js
  7. 4
      src/components/IsUnlocked.js
  8. 13
      src/components/OperationsList/index.js
  9. 301
      src/components/RequestAmount/index.js
  10. 2
      src/components/base/FormattedVal/__tests__/FormattedVal.test.js
  11. 5
      src/components/base/FormattedVal/index.js
  12. 2
      src/components/base/InputCurrency/index.js
  13. 2
      src/components/modals/AddAccount/index.js
  14. 3
      src/components/modals/OperationDetails.js
  15. 10
      src/components/modals/Send/01-step-amount.js
  16. 3
      src/components/modals/Send/Footer.js
  17. 26
      src/components/modals/Send/index.js
  18. 54
      src/helpers/__tests__/balance.test.js
  19. 7
      src/helpers/balance.js
  20. 11
      src/helpers/db.js
  21. 1
      src/main/bridge.js
  22. 24
      src/main/counterValuesSync.js
  23. 88
      src/reducers/counterValues.js
  24. 8
      src/reducers/settings.js
  25. 4
      src/renderer/createStore.js
  26. 27
      src/renderer/events.js
  27. 1
      src/renderer/index.js
  28. 6
      yarn.lock

3
package.json

@ -48,7 +48,7 @@
"@ledgerhq/hw-app-eth": "^4.7.3", "@ledgerhq/hw-app-eth": "^4.7.3",
"@ledgerhq/hw-transport": "^4.7.3", "@ledgerhq/hw-transport": "^4.7.3",
"@ledgerhq/hw-transport-node-hid": "^4.7.3", "@ledgerhq/hw-transport-node-hid": "^4.7.3",
"@ledgerhq/wallet-common": "^0.10.0", "@ledgerhq/wallet-common": "^0.10.1",
"axios": "^0.18.0", "axios": "^0.18.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bitcoinjs-lib": "^3.3.2", "bitcoinjs-lib": "^3.3.2",
@ -112,6 +112,7 @@
"babel-loader": "^8.0.0-beta.2", "babel-loader": "^8.0.0-beta.2",
"babel-plugin-module-resolver": "^3.1.1", "babel-plugin-module-resolver": "^3.1.1",
"babel-plugin-styled-components": "^1.5.0", "babel-plugin-styled-components": "^1.5.0",
"babel-polyfill": "^6.26.0",
"chalk": "^2.3.1", "chalk": "^2.3.1",
"chance": "^1.0.13", "chance": "^1.0.13",
"concurrently": "^3.5.1", "concurrently": "^3.5.1",

77
src/actions/counterValues.js

@ -1,15 +1,11 @@
// @flow // @flow
import axios from 'axios' import { getFiatUnit } from '@ledgerhq/currencies'
import moment from 'moment' import { fetchHistodayCounterValuesMultiple } from '@ledgerhq/wallet-common/lib/api/countervalue'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import type { Currency } from '@ledgerhq/currencies'
import type { Dispatch } from 'redux' import type { Dispatch } from 'redux'
import { serializeCounterValues } from 'reducers/counterValues'
import get from 'lodash/get'
import db from 'helpers/db' import db from 'helpers/db'
export type InitCounterValues = () => { type: string, payload: Object } export type InitCounterValues = () => { type: string, payload: Object }
@ -24,60 +20,17 @@ export const updateCounterValues: UpdateCounterValues = payload => ({
payload, payload,
}) })
export type UpdateLastCounterValueBySymbol = (string, Object) => { type: string, payload: Object } export type FetchCounterValues = (?(Currency[])) => (Dispatch<*>, Function) => Promise<any>
export const updateLastCounterValueBySymbol: UpdateLastCounterValueBySymbol = (symbol, value) => ({ export const fetchCounterValues: FetchCounterValues = (currencies: ?(Currency[])) => async (
type: 'DB:UPDATE_LAST_COUNTER_VALUE', dispatch,
payload: { getState,
symbol, ) => {
value, const { accounts, settings } = getState()
}, if (!currencies) {
}) currencies = accounts.map(a => a.currency)
export type FetchCounterValues = (?number) => (Dispatch<*>, Function) => Promise<any>
export const fetchCounterValues: FetchCounterValues = coinType => (dispatch, getState) => {
const { accounts, counterValues, settings } = getState()
const { counterValue } = settings
let coinTypes = []
if (!coinType) {
coinTypes = [...new Set(accounts.map(a => a.coinType))]
} else {
coinTypes = [coinType]
} }
const { counterValue } = settings
const today = moment().format('YYYY-MM-DD') const fiatUnit = getFiatUnit(counterValue)
const counterValues = await fetchHistodayCounterValuesMultiple(currencies, fiatUnit)
const fetchCounterValuesByCoinType = coinType => { dispatch(updateCounterValues(counterValues))
const { code } = getDefaultUnitByCoinType(coinType)
const todayCounterValues = get(counterValues, `${code}-${counterValue}.${today}`, null)
if (todayCounterValues !== null) {
return null
}
return axios
.get(
`https://min-api.cryptocompare.com/data/histoday?&extraParams=ledger-test&fsym=${code}&tsym=${counterValue}&allData=1`,
)
.then(({ data }) => ({
symbol: `${code}-${counterValue}`,
values: data.Data.map(d => [moment(d.time * 1000).format('YYYY-MM-DD'), d.close]),
}))
}
return Promise.all(coinTypes.map(fetchCounterValuesByCoinType)).then(results => {
const newCounterValues = serializeCounterValues(
results.reduce((r, v) => {
if (v !== null) {
r[v.symbol] = v.values
}
return r
}, {}),
)
if (Object.keys(newCounterValues).length !== 0) {
dispatch(updateCounterValues(newCounterValues))
}
})
} }

4
src/components/AccountPage/index.js

@ -15,7 +15,7 @@ import type { T } from 'types/common'
import { darken } from 'styles/helpers' import { darken } from 'styles/helpers'
import { getAccountById } from 'reducers/accounts' import { getAccountById } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings' import { getCounterValueCode } from 'reducers/settings'
import { openModal } from 'reducers/modals' import { openModal } from 'reducers/modals'
import IconControls from 'icons/Controls' import IconControls from 'icons/Controls'
@ -50,7 +50,7 @@ const ButtonSettings = styled(Button).attrs({
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
account: getAccountById(state, props.match.params.id), account: getAccountById(state, props.match.params.id),
counterValue: getCounterValue(state), counterValue: getCounterValueCode(state),
}) })
const mapDispatchToProps = { const mapDispatchToProps = {

40
src/components/CounterValue/index.js

@ -3,26 +3,25 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import moment from 'moment' import moment from 'moment'
import type { Unit, Currency } from '@ledgerhq/currencies'
import isNaN from 'lodash/isNaN' import { getCounterValueCode } from 'reducers/settings'
import { calculateCounterValueSelector } from 'reducers/counterValues'
import type { Unit } from '@ledgerhq/currencies'
import { getCounterValue } from 'reducers/settings'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
const mapStateToProps = state => ({ const mapStateToProps = state => ({
counterValue: getCounterValue(state), counterValueCode: getCounterValueCode(state),
counterValues: state.counterValues, getCounterValue: calculateCounterValueSelector(state),
}) })
type Props = { type Props = {
formatValue: boolean, formatValue: boolean,
counterValue: string, counterValueCode: string,
counterValues: Object, getCounterValue: Function,
time?: Date | string | number, time?: Date | string | number,
unit: Unit, unit: Unit,
currency: Currency,
value: number, value: number,
} }
@ -34,17 +33,22 @@ export class CounterValue extends PureComponent<Props> {
} }
render() { render() {
const { formatValue, value, unit, counterValue, counterValues, time, ...props } = this.props const {
formatValue,
const cValues = counterValues[`${unit.code}-${counterValue}`] value,
currency,
const v = isNaN(Number(value)) unit,
? 0 counterValueCode,
: (time ? cValues.byDate[moment(time).format('YYYY-MM-DD')] : cValues.list[0][1]) * time,
(value / 10 ** unit.magnitude) getCounterValue,
...props
} = this.props
const date = moment(time).format('YYYY-MM-DD')
const v = getCounterValue(currency, counterValueCode)(value, date)
return formatValue ? ( return formatValue ? (
<FormattedVal val={v} fiat={counterValue} showCode alwaysShowSign {...props} /> <FormattedVal val={v} fiat={counterValueCode} showCode alwaysShowSign {...props} />
) : ( ) : (
v v
) )

18
src/components/CounterValue/stories.js

@ -1,30 +1,38 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { boolean, text } from '@storybook/addon-knobs' import { boolean, text } from '@storybook/addon-knobs'
import createHistory from 'history/createHashHistory'
import { CounterValue } from 'components/CounterValue' import { CounterValue } from 'components/CounterValue'
import { calculateCounterValueSelector } from 'reducers/counterValues'
import createStore from 'renderer/createStore'
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
const currency = getCurrencyByCoinType(0)
const unit = getDefaultUnitByCoinType(0) const unit = getDefaultUnitByCoinType(0)
const counterValue = 'USD' const counterValue = 'USD'
const counterValues = { const counterValues = {
'BTC-USD': { BTC: {
byDate: { USD: {
'2018-01-09': 10000, '2018-01-09': 10000,
}, },
list: [['2018-01-09', 10000]],
}, },
} }
const store = createStore(createHistory(), { counterValues })
const getCounterValue = calculateCounterValueSelector(store.getState())
stories.add('CounterValue', () => ( stories.add('CounterValue', () => (
<CounterValue <CounterValue
counterValue={counterValue} getCounterValue={getCounterValue}
counterValueCode={counterValue}
counterValues={counterValues} counterValues={counterValues}
currency={currency}
unit={unit} unit={unit}
formatValue={boolean('formatValue', true)} formatValue={boolean('formatValue', true)}
value={Number(text('value', '100000000'))} value={Number(text('value', '100000000'))}

4
src/components/DashboardPage/index.js

@ -14,7 +14,7 @@ import sortBy from 'lodash/sortBy'
import type { T } from 'types/common' import type { T } from 'types/common'
import { getVisibleAccounts } from 'reducers/accounts' import { getVisibleAccounts } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings' import { getCounterValueCode } from 'reducers/settings'
import { updateOrderAccounts } from 'actions/accounts' import { updateOrderAccounts } from 'actions/accounts'
import { saveSettings } from 'actions/settings' import { saveSettings } from 'actions/settings'
@ -31,7 +31,7 @@ import AccountsOrder from './AccountsOrder'
const mapStateToProps = state => ({ const mapStateToProps = state => ({
accounts: getVisibleAccounts(state), accounts: getVisibleAccounts(state),
counterValue: getCounterValue(state), counterValue: getCounterValueCode(state),
}) })
const mapDispatchToProps = { const mapDispatchToProps = {

4
src/components/IsUnlocked.js

@ -22,7 +22,7 @@ import { setEncryptionKey } from 'helpers/db'
import { fetchAccounts } from 'actions/accounts' import { fetchAccounts } from 'actions/accounts'
import { getAccounts } from 'reducers/accounts' import { getAccounts } from 'reducers/accounts'
import { isLocked, unlock } from 'reducers/application' import { isLocked, unlock } from 'reducers/application'
import { getCounterValue } from 'reducers/settings' import { getCounterValueCode } from 'reducers/settings'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Input from 'components/base/Input' import Input from 'components/base/Input'
@ -47,7 +47,7 @@ type State = {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
accounts: getAccounts(state), accounts: getAccounts(state),
counterValue: getCounterValue(state), counterValue: getCounterValueCode(state),
isLocked: isLocked(state), isLocked: isLocked(state),
settings: state.settings, settings: state.settings,
}) })

13
src/components/OperationsList/index.js

@ -129,7 +129,7 @@ const Operation = ({
op: OperationType, op: OperationType,
withAccount?: boolean, withAccount?: boolean,
}) => { }) => {
const { unit } = account const { unit, currency } = account
const time = moment(op.date) const time = moment(op.date)
const Icon = getIconByCoinType(account.currency.coinType) const Icon = getIconByCoinType(account.currency.coinType)
const type = op.amount > 0 ? 'from' : 'to' const type = op.amount > 0 ? 'from' : 'to'
@ -179,7 +179,7 @@ const Operation = ({
<Cell grow shrink style={{ display: 'block' }}> <Cell grow shrink style={{ display: 'block' }}>
<Address value={op.address} /> <Address value={op.address} />
</Cell> </Cell>
<Cell size={AMOUNT_COL_SIZE}> <Cell size={AMOUNT_COL_SIZE} justify="flex-end">
<Box alignItems="flex-end"> <Box alignItems="flex-end">
<FormattedVal <FormattedVal
val={op.amount} val={op.amount}
@ -189,7 +189,14 @@ const Operation = ({
alwaysShowSign alwaysShowSign
color={op.amount < 0 ? 'smoke' : 'positiveGreen'} color={op.amount < 0 ? 'smoke' : 'positiveGreen'}
/> />
<CounterValue color="grey" fontSize={3} time={time} unit={unit} value={op.amount} /> <CounterValue
color="grey"
fontSize={3}
time={time}
currency={currency}
unit={unit}
value={op.amount}
/>
</Box> </Box>
</Cell> </Cell>
</OperationRaw> </OperationRaw>

301
src/components/RequestAmount/index.js

@ -5,16 +5,13 @@ import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { getDefaultUnitByCoinType, getFiatUnit } from '@ledgerhq/currencies' import type { Account, CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Unit } from '@ledgerhq/currencies'
import isNaN from 'lodash/isNaN'
import noop from 'lodash/noop'
import type { T } from 'types/common' import type { T } from 'types/common'
import { getCounterValue } from 'reducers/settings' import { getCounterValueFiatUnit } from 'reducers/settings'
import { getLastCounterValueBySymbol } from 'reducers/counterValues' import { calculateCounterValueSelector, reverseCounterValueSelector } from 'reducers/counterValues'
import InputCurrency from 'components/base/InputCurrency' import InputCurrency from 'components/base/InputCurrency'
import Button from 'components/base/Button' import Button from 'components/base/Button'
@ -27,6 +24,7 @@ const InputRight = styled(Box).attrs({
justifyContent: 'center', justifyContent: 'center',
pr: 3, pr: 3,
})`` })``
const InputCenter = styled(Box).attrs({ const InputCenter = styled(Box).attrs({
ff: 'Rubik', ff: 'Rubik',
color: 'graphite', color: 'graphite',
@ -37,258 +35,119 @@ const InputCenter = styled(Box).attrs({
width: 30px; width: 30px;
` `
const mapStateToProps = (state, { account }) => { const mapStateToProps = state => ({
const counterValue = getCounterValue(state) rightUnit: getCounterValueFiatUnit(state),
const unit = getDefaultUnitByCoinType(account.coinType) getCounterValue: calculateCounterValueSelector(state),
const symbol = `${unit.code}-${counterValue}` getReverseCounterValue: reverseCounterValueSelector(state),
return { })
counterValue,
lastCounterValue: getLastCounterValueBySymbol(symbol, state),
}
}
function maxUnitDigits(unit, value) {
const [leftDigits, rightDigits] = value.toString().split('.')
return Number(`${leftDigits}${rightDigits ? `.${rightDigits.slice(0, unit.magnitude)}` : ''}`)
}
function calculateMax(props) { type Props = {
const { account, counterValue, lastCounterValue } = props // translation
t: T,
const unit = getUnit({ account, counterValue })
const leftMax = account.balance / 10 ** unit.left.magnitude
return {
left: account.balance / 10 ** unit.left.magnitude,
right: maxUnitDigits(unit.right, leftMax * lastCounterValue),
}
}
function getUnit({ account, counterValue }) {
return {
left: getDefaultUnitByCoinType(account.coinType),
right: getFiatUnit(counterValue),
}
}
function calculateValues({
dir,
value,
max,
lastCounterValue,
}: {
dir: string,
value: Object,
max: Object,
lastCounterValue: number,
}) {
const v = value[dir]
const getMax = (d, v) => {
const result = v > max[d] ? max[d] : v
return isNaN(result) ? '0' : result.toString()
}
const newValue = {}
if (dir === 'left') {
newValue.left = v === '' ? v : getMax('left', v)
newValue.right = getMax('right', Number(v) * lastCounterValue)
}
if (dir === 'right') { // left value (always the one which is returned)
newValue.left = getMax('left', Number(v) / lastCounterValue) value: number,
newValue.right = v === '' ? v : getMax('right', v)
}
return newValue // max left value
} max: number,
type Direction = 'left' | 'right' // change handler
onChange: ({ left: number, right: number }) => void,
type Props = { // used to determine the left input unit
account: Account, account: Account,
counterValue: string,
lastCounterValue: number, // eslint-disable-line react/no-unused-prop-types
onChange: Function,
t: T,
value: Object,
}
export type DoubleVal = { // used to determine the right input unit
left: number, // retrieved via selector (take the chosen countervalue unit)
right: number, rightUnit: Unit,
// used to calculate the opposite field value (right & left)
getCounterValue: CalculateCounterValue,
getReverseCounterValue: CalculateCounterValue,
} }
type State = { type State = {
max: DoubleVal, leftUnit: Unit,
value: { rightUnit: Unit,
left: string | number, leftValue: number,
right: string | number, rightValue: number,
},
} }
export class RequestAmount extends PureComponent<Props, State> { export class RequestAmount extends PureComponent<Props, State> {
static defaultProps = {
onChange: noop,
value: {},
}
constructor(props: Props) { constructor(props: Props) {
super() super(props)
this.props = props
const max = calculateMax(props) const { account, rightUnit, value, getCounterValue } = this.props
let v = { const rawLeftValue = value * 10 ** account.unit.magnitude
left: 0, const rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
right: 0, const rightValue = rawRightValue / 10 ** rightUnit.magnitude
}
if (props.value.left) {
v = calculateValues({
...props,
dir: 'left',
max,
})
}
if (props.value.right) {
v = calculateValues({
...props,
dir: 'right',
max,
})
}
this.state = { this.state = {
max, leftUnit: account.unit,
value: v, rightUnit,
} leftValue: value,
} rightValue,
componentWillReceiveProps(nextProps: Props) {
if (this.props.account !== nextProps.account) {
const max = calculateMax(nextProps)
this.setState({
max,
value: calculateValues({
...nextProps,
dir: 'left',
max,
}),
})
} }
} }
componentDidUpdate(prevProps: Props) {
this.updateValueWithProps(prevProps, this.props)
}
handleChangeAmount = (dir: Direction) => (v: number | string) => {
const { onChange, value, account, counterValue, ...otherProps } = this.props
const { max } = this.state
const otherDir = dir === 'left' ? 'right' : 'left'
const unit = getUnit({
account,
counterValue,
})
const newValue = calculateValues({
...otherProps,
dir,
value: {
[dir]: v.toString(),
},
max,
})
newValue[otherDir] = maxUnitDigits(unit[otherDir], newValue[otherDir]).toString()
this.setState({
value: newValue,
})
onChange({
values: newValue,
rawValues: {
left: Number(newValue.left) * 10 ** unit.left.magnitude,
right: Number(newValue.right),
},
})
}
handleClickMax = () => { handleClickMax = () => {
const { account } = this.props const leftValue = this.props.max / 10 ** this.props.account.unit.magnitude
this.handleChangeAmount('left')(account.balance) this.handleChangeAmount('left')(leftValue)
this.setState({ leftValue })
} }
updateValueWithProps = (props: Props, nextProps: Props) => { handleChangeAmount = (changedField: string) => (val: number) => {
if ( const { getCounterValue, getReverseCounterValue, account, max, onChange } = this.props
props.value.left !== nextProps.value.left && const { rightUnit } = this.state
nextProps.value.left !== this.state.value.left if (changedField === 'left') {
) { let rawLeftValue = val * 10 ** account.unit.magnitude
this.setState({ if (rawLeftValue > max) {
value: calculateValues({ rawLeftValue = max
...nextProps, }
dir: 'left', const leftValue = rawLeftValue / 10 ** account.unit.magnitude
max: this.state.max, const rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
}), const rightValue = rawRightValue / 10 ** rightUnit.magnitude
}) this.setState({ rightValue, leftValue })
} onChange({ left: rawLeftValue, right: rawRightValue })
} else if (changedField === 'right') {
if ( let rawRightValue = val * 10 ** rightUnit.magnitude
props.value.right !== nextProps.value.right && let rawLeftValue = getReverseCounterValue(account.currency, rightUnit)(rawRightValue)
nextProps.value.right !== this.state.value.right if (rawLeftValue > max) {
) { rawLeftValue = max
this.setState({ rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
value: calculateValues({ }
...nextProps, const rightValue = rawRightValue / 10 ** rightUnit.magnitude
dir: 'right', const leftValue = rawLeftValue / 10 ** account.unit.magnitude
max: this.state.max, this.setState({ rightValue, leftValue })
}), onChange({ left: rawLeftValue, right: rawRightValue })
})
} }
} }
render() { render() {
const { account, counterValue, t } = this.props const { t } = this.props
const { value } = this.state const { leftUnit, rightUnit, leftValue, rightValue } = this.state
const unit = getUnit({
account,
counterValue,
})
return ( return (
<Box horizontal flow={5}> <Box horizontal flow="5">
<Box horizontal> <Box horizontal align="center">
<InputCurrency <InputCurrency
containerProps={{ containerProps={{ style: { width: 156 } }}
style: { unit={leftUnit}
width: 156, value={leftValue}
},
}}
unit={unit.left}
value={value.left}
onChange={this.handleChangeAmount('left')} onChange={this.handleChangeAmount('left')}
renderRight={<InputRight>{unit.left.code}</InputRight>} renderRight={<InputRight>{leftUnit.code}</InputRight>}
/> />
<InputCenter>=</InputCenter> <InputCenter>=</InputCenter>
<InputCurrency <InputCurrency
containerProps={{ containerProps={{ style: { width: 156 } }}
style: { unit={rightUnit}
width: 156, value={rightValue}
},
}}
unit={unit.right}
value={value.right}
onChange={this.handleChangeAmount('right')} onChange={this.handleChangeAmount('right')}
renderRight={<InputRight>{unit.right.code}</InputRight>} renderRight={<InputRight>{rightUnit.code}</InputRight>}
/> />
</Box> </Box>
<Box grow justifyContent="flex-end"> <Box grow justify="flex-end">
<Button primary onClick={this.handleClickMax}> <Button primary onClick={this.handleClickMax}>
{t('common:max')} {t('common:max')}
</Button> </Button>

2
src/components/base/FormattedVal/__tests__/FormattedVal.test.js

@ -37,7 +37,7 @@ describe('components', () => {
}) })
it('renders a fiat', () => { it('renders a fiat', () => {
const component = <FormattedVal fiat="USD" val={20} /> const component = <FormattedVal fiat="USD" val={2000} />
const tree = render(component) const tree = render(component)
expect(tree).toMatchSnapshot() expect(tree).toMatchSnapshot()
}) })

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

@ -26,8 +26,8 @@ type Props = {
} }
function FormattedVal(props: Props) { function FormattedVal(props: Props) {
const { disableRounding, fiat, isPercent, alwaysShowSign, showCode, ...p } = props const { val, disableRounding, fiat, isPercent, alwaysShowSign, showCode, ...p } = props
let { val, unit } = props let { unit } = props
if (isUndefined(val)) { if (isUndefined(val)) {
throw new Error('FormattedVal require a `val` prop. Received `undefined`') throw new Error('FormattedVal require a `val` prop. Received `undefined`')
@ -42,7 +42,6 @@ function FormattedVal(props: Props) {
} else { } else {
if (fiat) { if (fiat) {
unit = getFiatUnit(fiat) unit = getFiatUnit(fiat)
val *= 10 ** unit.magnitude
} else if (!unit) { } else if (!unit) {
return '' return ''
} }

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

@ -14,7 +14,7 @@ import Select from 'components/base/Select'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/currencies'
function parseValue(value) { function parseValue(value) {
return value.toString().replace(/,/, '.') return value.toString().replace(/,/g, '.')
} }
function format(unit: Unit, value: Value) { function format(unit: Unit, value: Value) {

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

@ -306,7 +306,7 @@ class AddAccountModal extends PureComponent<Props, State> {
fetchingCounterValues: true, fetchingCounterValues: true,
}) })
await fetchCounterValues(currency.coinType) await fetchCounterValues([currency])
this.setState({ this.setState({
fetchingCounterValues: false, fetchingCounterValues: false,

3
src/components/modals/OperationDetails.js

@ -54,7 +54,7 @@ const OperationDetails = ({ t }: { t: T }) => (
render={({ data, onClose }) => { render={({ data, onClose }) => {
const { operation, account, type } = data const { operation, account, type } = data
const { name, unit, minConfirmations } = account const { name, unit, currency, minConfirmations } = account
const { id, amount, confirmations, date, from, to } = operation const { id, amount, confirmations, date, from, to } = operation
const isConfirmed = confirmations >= minConfirmations const isConfirmed = confirmations >= minConfirmations
@ -92,6 +92,7 @@ const OperationDetails = ({ t }: { t: T }) => (
style={{ lineHeight: 1 }} style={{ lineHeight: 1 }}
time={date} time={date}
unit={unit} unit={unit}
currency={currency}
value={amount} value={amount}
/> />
</Box> </Box>

10
src/components/modals/Send/01-step-amount.js

@ -5,7 +5,6 @@ import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common' import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import CheckBox from 'components/base/CheckBox' import CheckBox from 'components/base/CheckBox'
@ -23,7 +22,7 @@ type PropsStepAmount = {
account: Account | null, account: Account | null,
onChange: Function, onChange: Function,
recipientAddress: string, recipientAddress: string,
amount: DoubleVal, amount: { left: number, right: number },
fees: { fees: {
value: number, value: number,
unit: Unit | null, unit: Unit | null,
@ -61,7 +60,12 @@ function StepAmount(props: PropsStepAmount) {
{/* AMOUNT */} {/* AMOUNT */}
<Box flow={1}> <Box flow={1}>
<Label>{t('send:steps.amount.amount')}</Label> <Label>{t('send:steps.amount.amount')}</Label>
<RequestAmount account={account} onChange={onChange('amount')} value={amount} /> <RequestAmount
max={account.balance - 0}
account={account}
onChange={onChange('amount')}
value={amount.left}
/>
</Box> </Box>
{/* FEES */} {/* FEES */}

3
src/components/modals/Send/Footer.js

@ -4,7 +4,6 @@ import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import { ModalFooter } from 'components/base/Modal' import { ModalFooter } from 'components/base/Modal'
import Box from 'components/base/Box' import Box from 'components/base/Box'
@ -16,7 +15,7 @@ import Text from 'components/base/Text'
type Props = { type Props = {
t: T, t: T,
account: Account, account: Account,
amount: DoubleVal, amount: { left: number, right: number },
onNext: Function, onNext: Function,
canNext: boolean, canNext: boolean,
counterValue: string, counterValue: string,

26
src/components/modals/Send/index.js

@ -9,11 +9,10 @@ import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common' import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import { MODAL_SEND } from 'config/constants' import { MODAL_SEND } from 'config/constants'
import { getCounterValue } from 'reducers/settings' import { getCounterValueCode } from 'reducers/settings'
import Breadcrumb from 'components/Breadcrumb' import Breadcrumb from 'components/Breadcrumb'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal' import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
@ -26,7 +25,7 @@ import StepVerification from './03-step-verification'
import StepConfirmation from './04-step-confirmation' import StepConfirmation from './04-step-confirmation'
const mapStateToProps = state => ({ const mapStateToProps = state => ({
counterValue: getCounterValue(state), counterValue: getCounterValueCode(state),
}) })
type Props = { type Props = {
@ -37,10 +36,7 @@ type Props = {
type State = { type State = {
stepIndex: number, stepIndex: number,
isDeviceReady: boolean, isDeviceReady: boolean,
amount: { amount: { left: number, right: number },
values: DoubleVal,
rawValues: DoubleVal,
},
account: Account | null, account: Account | null,
recipientAddress: string, recipientAddress: string,
fees: { fees: {
@ -63,14 +59,8 @@ const INITIAL_STATE = {
account: null, account: null,
recipientAddress: '', recipientAddress: '',
amount: { amount: {
values: { left: 0,
left: 0, right: 0,
right: 0,
},
rawValues: {
left: 0,
right: 0,
},
}, },
fees: { fees: {
value: 0, value: 0,
@ -90,7 +80,7 @@ class SendModal extends PureComponent<Props, State> {
// informations // informations
if (stepIndex === 0) { if (stepIndex === 0) {
const { amount, recipientAddress } = this.state const { amount, recipientAddress } = this.state
return !!amount.rawValues.left && !!recipientAddress && !!account return !!amount.left && !!recipientAddress && !!account
} }
// connect device // connect device
@ -123,7 +113,7 @@ class SendModal extends PureComponent<Props, State> {
const { Comp } = step const { Comp } = step
const stepProps = { const stepProps = {
...othersState, ...othersState,
amount: amount.values, amount,
account: account || acc, account: account || acc,
} }
@ -154,7 +144,7 @@ class SendModal extends PureComponent<Props, State> {
canNext={canNext} canNext={canNext}
onNext={this.handleNextStep} onNext={this.handleNextStep}
account={acc} account={acc}
amount={amount.rawValues} amount={amount}
t={t} t={t}
/> />
)} )}

54
src/helpers/__tests__/balance.test.js

@ -1,13 +1,13 @@
import { getBalanceHistoryForAccount, getBalanceHistoryForAccounts } from 'helpers/balance' import { getBalanceHistoryForAccount, getBalanceHistoryForAccounts } from 'helpers/balance'
const counterValues = { const counterValues = {
'BTC-USD': { BTC: {
byDate: { USD: {
'2018-01-01': 1000, '2018-01-01': 1,
'2018-01-02': 2000, '2018-01-02': 2,
'2018-01-03': 3000, '2018-01-03': 3,
'2018-01-04': 4000, '2018-01-04': 4,
'2018-01-05': 5000, '2018-01-05': 5,
}, },
}, },
} }
@ -18,8 +18,8 @@ describe('helpers > balance', () => {
const account = { const account = {
coinType: 0, coinType: 0,
balanceByDay: { balanceByDay: {
'2018-01-01': 100000000, '2018-01-01': 1,
'2018-01-02': 200000000, '2018-01-02': 2,
}, },
} }
@ -36,8 +36,8 @@ describe('helpers > balance', () => {
}) })
expect(balances).toEqual([ expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 }, { date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 4000 }, { date: '2018-01-02', balance: 4 },
]) ])
}) })
@ -45,8 +45,8 @@ describe('helpers > balance', () => {
const account = { const account = {
coinType: 0, coinType: 0,
balanceByDay: { balanceByDay: {
'2018-01-01': 100000000, '2018-01-01': 1,
'2018-01-03': 200000000, '2018-01-03': 2,
}, },
} }
@ -63,9 +63,9 @@ describe('helpers > balance', () => {
}) })
expect(balances).toEqual([ expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 }, { date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 2000 }, { date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 6000 }, { date: '2018-01-03', balance: 6 },
]) ])
}) })
@ -73,7 +73,7 @@ describe('helpers > balance', () => {
const account = { const account = {
coinType: 0, coinType: 0,
balanceByDay: { balanceByDay: {
'2018-01-01': 100000000, '2018-01-01': 1,
}, },
} }
@ -90,8 +90,8 @@ describe('helpers > balance', () => {
}) })
expect(balances).toEqual([ expect(balances).toEqual([
{ date: '2018-01-02', balance: 2000 }, { date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 3000 }, { date: '2018-01-03', balance: 3 },
]) ])
}) })
}) })
@ -101,16 +101,16 @@ describe('helpers > balance', () => {
const account1 = { const account1 = {
coinType: 0, coinType: 0,
balanceByDay: { balanceByDay: {
'2018-01-01': 100000000, '2018-01-01': 1,
'2018-01-02': 200000000, '2018-01-02': 2,
}, },
} }
const account2 = { const account2 = {
coinType: 0, coinType: 0,
balanceByDay: { balanceByDay: {
'2018-01-02': 500000000, '2018-01-02': 5,
'2018-01-04': 600000000, '2018-01-04': 6,
}, },
} }
@ -127,10 +127,10 @@ describe('helpers > balance', () => {
}) })
expect(balances).toEqual([ expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 }, { date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 14000 }, { date: '2018-01-02', balance: 14 },
{ date: '2018-01-03', balance: 21000 }, { date: '2018-01-03', balance: 21 },
{ date: '2018-01-04', balance: 32000 }, { date: '2018-01-04', balance: 32 },
]) ])
}) })
}) })

7
src/helpers/balance.js

@ -1,6 +1,7 @@
// @flow // @flow
import moment from 'moment' import moment from 'moment'
import get from 'lodash/get'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/wallet-common/lib/types'
@ -69,7 +70,7 @@ export function getBalanceHistoryForAccount({
interval: DateInterval, interval: DateInterval,
}): Array<BalanceHistoryDay> { }): Array<BalanceHistoryDay> {
const unit = getDefaultUnitByCoinType(account.coinType) const unit = getDefaultUnitByCoinType(account.coinType)
const counterVals = counterValues[`${unit.code}-${counterValue}`].byDate const counterVals = get(counterValues, `${unit.code}.${counterValue}`)
let lastBalance = getBalanceAtIntervalStart(account, interval) let lastBalance = getBalanceAtIntervalStart(account, interval)
return mapInterval(interval, date => { return mapInterval(interval, date => {
let balance = 0 let balance = 0
@ -81,11 +82,11 @@ export function getBalanceHistoryForAccount({
// if we don't have data on account balance for that day, // if we don't have data on account balance for that day,
// we take the prev day // we take the prev day
if (isUndefined(account.balanceByDay[date])) { if (isUndefined(account.balanceByDay[date])) {
balance = lastBalance === null ? 0 : lastBalance / 10 ** unit.magnitude * counterVals[date] balance = lastBalance === null ? 0 : lastBalance * counterVals[date]
} else { } else {
const b = account.balanceByDay[date] const b = account.balanceByDay[date]
lastBalance = b lastBalance = b
balance = b / 10 ** unit.magnitude * counterVals[date] balance = b * counterVals[date]
} }
if (isNaN(balance)) { if (isNaN(balance)) {

11
src/helpers/db.js

@ -5,7 +5,6 @@ import set from 'lodash/set'
import get from 'lodash/get' import get from 'lodash/get'
import { serializeAccounts, deserializeAccounts } from 'reducers/accounts' import { serializeAccounts, deserializeAccounts } from 'reducers/accounts'
import { serializeCounterValues, deserializeCounterValues } from 'reducers/counterValues'
type DBKey = 'settings' | 'accounts' | 'counterValues' type DBKey = 'settings' | 'accounts' | 'counterValues'
@ -35,16 +34,6 @@ function middleware(type, key, data: any) {
} }
} }
if (key === 'counterValues') {
if (type === 'get') {
data = serializeCounterValues(data)
}
if (type === 'set') {
data = deserializeCounterValues(data)
}
}
return data return data
} }

1
src/main/bridge.js

@ -1,5 +1,6 @@
// @flow // @flow
import 'babel-polyfill'
import { fork } from 'child_process' import { fork } from 'child_process'
import { BrowserWindow, ipcMain } from 'electron' import { BrowserWindow, ipcMain } from 'electron'
import objectPath from 'object-path' import objectPath from 'object-path'

24
src/main/counterValuesSync.js

@ -1,24 +1,14 @@
// @flow // @flow
import axios from 'axios' import { fetchCurrentCounterValues } from '@ledgerhq/wallet-common/lib/api/countervalue'
type SendFunction = (type: string, data: *) => void type SendFunction = (type: string, data: *) => void
export default async (send: SendFunction, { counterValue, currencies }: Object) => { export default async (send: SendFunction, { counterValue, currencies }: Object) => {
const data = await axios try {
.get( const data = await fetchCurrentCounterValues(currencies, counterValue)
`https://min-api.cryptocompare.com/data/pricemulti?extraParams=ledger-test&fsyms=${currencies.join( send('counterValues.update', data)
',', } catch (err) {
)}&tsyms=${counterValue}`, console.error(err) // eslint-disable-line no-console
) }
.then(({ data }) =>
currencies.reduce((result, code) => {
result.push({
symbol: `${code}-${counterValue}`,
value: data[code][counterValue],
})
return result
}, []),
)
send('counterValues.update', data)
} }

88
src/reducers/counterValues.js

@ -1,73 +1,41 @@
// @flow // @flow
import { handleActions } from 'redux-actions' import { handleActions } from 'redux-actions'
import merge from 'lodash/merge'
export type CounterValuesState = { import get from 'lodash/get'
[string]: { import {
byDate: Object, makeCalculateCounterValue,
list: Array<[string, number]>, makeReverseCounterValue,
}, formatCounterValueDay,
} } from '@ledgerhq/wallet-common/lib/helpers/countervalue'
import type { CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types'
import type { State } from 'reducers'
export type CounterValuesState = {}
const state: CounterValuesState = {} const state: CounterValuesState = {}
const handlers = { const handlers = {
UPDATE_COUNTER_VALUES: ( UPDATE_COUNTER_VALUES: (state, { payload: counterValues }) => merge(state, counterValues),
state: CounterValuesState,
{ payload: counterValues }: { payload: CounterValuesState },
): CounterValuesState => ({
...state,
...counterValues,
}),
UPDATE_LAST_COUNTER_VALUE: (
state: CounterValuesState,
{ payload: { symbol, value } }: { payload: { symbol: string, value: number } },
): CounterValuesState => {
// We update only last value (newer)
if (state[symbol]) {
const [date] = state[symbol].list[0]
// [0] date, [1] value, only update value
state[symbol].list[0][1] = value
// Keep the same value for byDate object
state[symbol].byDate[date] = value
// Update reference for a proper update
return { ...state }
}
return state
},
} }
export function getLastCounterValueBySymbol( const getPairHistory = state => (coinTicker, fiat) => {
symbol: string, const byDate = get(state, `counterValues.${coinTicker}.${fiat}`)
state: { counterValues: CounterValuesState }, return date => {
): number { if (!byDate) {
return state.counterValues[symbol].list[0][1] return 0
}
export function serializeCounterValues(counterValues: Object) {
return Object.keys(counterValues).reduce((result, key) => {
const counterValue = counterValues[key].sort(([dateA], [dateB]) => (dateA < dateB ? 1 : -1))
result[key] = {
byDate: counterValue.reduce((r, [date, value]) => {
r[date] = value
return r
}, {}),
list: counterValue,
} }
if (!date) {
return result return byDate.latest || 0
}, {}) }
return byDate[formatCounterValueDay(date)] || 0
}
} }
export function deserializeCounterValues(counterValues: Object) { export const calculateCounterValueSelector = (state: State): CalculateCounterValue =>
return Object.keys(counterValues).reduce((result, key) => { makeCalculateCounterValue(getPairHistory(state))
const counterValue = counterValues[key]
result[key] = counterValue.list export const reverseCounterValueSelector = (state: State): CalculateCounterValue =>
return result makeReverseCounterValue(getPairHistory(state))
}, {})
}
export default handleActions(handlers, state) export default handleActions(handlers, state)

8
src/reducers/settings.js

@ -1,6 +1,7 @@
// @flow // @flow
import { handleActions } from 'redux-actions' import { handleActions } from 'redux-actions'
import { getFiatUnit } from '@ledgerhq/currencies'
import get from 'lodash/get' import get from 'lodash/get'
@ -34,9 +35,14 @@ const handlers: Object = {
export const hasPassword = (state: Object) => export const hasPassword = (state: Object) =>
get(state.settings, 'password.state', defaultState.password.state) get(state.settings, 'password.state', defaultState.password.state)
export const getCounterValue = (state: Object) =>
export const getCounterValueCode = (state: Object) =>
get(state.settings, 'counterValue', defaultState.counterValue) get(state.settings, 'counterValue', defaultState.counterValue)
export const getCounterValueFiatUnit = (state: Object) => getFiatUnit(getCounterValueCode(state))
export const getLanguage = (state: Object) => get(state.settings, 'language', defaultState.language) export const getLanguage = (state: Object) => get(state.settings, 'language', defaultState.language)
export const getOrderAccounts = (state: Object) => export const getOrderAccounts = (state: Object) =>
get(state.settings, 'orderAccounts', defaultState.orderAccounts) get(state.settings, 'orderAccounts', defaultState.orderAccounts)

4
src/renderer/createStore.js

@ -10,11 +10,11 @@ import db from 'middlewares/db'
import reducers from 'reducers' import reducers from 'reducers'
export default (history: HashHistory) => { export default (history: HashHistory, initialState: any) => {
const middlewares = [routerMiddleware(history), thunk, db] const middlewares = [routerMiddleware(history), thunk, db]
const enhancers = compose( const enhancers = compose(
applyMiddleware(...middlewares), applyMiddleware(...middlewares),
window.devToolsExtension ? window.devToolsExtension() : f => f, // eslint-disable-line window.devToolsExtension ? window.devToolsExtension() : f => f, // eslint-disable-line
) )
return createStore(reducers, undefined, enhancers) return createStore(reducers, initialState, enhancers)
} }

27
src/renderer/events.js

@ -3,17 +3,19 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import objectPath from 'object-path' import objectPath from 'object-path'
import debug from 'debug' import debug from 'debug'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import uniqBy from 'lodash/uniqBy'
import { getFiatUnit } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Currency, Unit } from '@ledgerhq/currencies'
import { CHECK_UPDATE_DELAY, SYNC_ACCOUNT_DELAY, SYNC_COUNTER_VALUES_DELAY } from 'config/constants' import { CHECK_UPDATE_DELAY, SYNC_ACCOUNT_DELAY, SYNC_COUNTER_VALUES_DELAY } from 'config/constants'
import { getAccounts, getAccountById } from 'reducers/accounts' import { getAccounts, getAccountById } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings' import { getCounterValueCode } from 'reducers/settings'
import { isLocked } from 'reducers/application' import { isLocked } from 'reducers/application'
import { setUpdateStatus } from 'reducers/update' import { setUpdateStatus } from 'reducers/update'
import { updateLastCounterValueBySymbol } from 'actions/counterValues' import { updateCounterValues } from 'actions/counterValues'
import { updateAccount } from 'actions/accounts' import { updateAccount } from 'actions/accounts'
import { updateDevices, addDevice, removeDevice } from 'actions/devices' import { updateDevices, addDevice, removeDevice } from 'actions/devices'
@ -106,15 +108,11 @@ export function stopSyncAccounts() {
clearTimeout(syncAccountsTimeout) clearTimeout(syncAccountsTimeout)
} }
export function startSyncCounterValues(counterValue: string, accounts: Account[]) { export function startSyncCounterValues(counterValueCode: string, accounts: Account[]) {
d.sync('Sync counterValues - start') d.sync('Sync counterValues - start')
const currencies: Currency[] = uniqBy(accounts.map(a => a.currency), 'code')
sendEvent('msg', 'counterValues.sync', { const counterValue: Unit = getFiatUnit(counterValueCode)
counterValue, sendEvent('msg', 'counterValues.sync', { currencies, counterValue })
currencies: [
...new Set(accounts.map(account => getDefaultUnitByCoinType(account.coinType).code)),
],
})
} }
export function stopSyncCounterValues() { export function stopSyncCounterValues() {
@ -206,12 +204,11 @@ export default ({ store, locked }: { store: Object, locked: boolean }) => {
}, },
counterValues: { counterValues: {
update: counterValues => { update: counterValues => {
counterValues.map(c => store.dispatch(updateLastCounterValueBySymbol(c.symbol, c.value))) store.dispatch(updateCounterValues(counterValues))
syncCounterValuesTimeout = setTimeout(() => { syncCounterValuesTimeout = setTimeout(() => {
const state = store.getState() const state = store.getState()
const accounts = getAccounts(state) const accounts = getAccounts(state)
const counterValue = getCounterValue(state) const counterValue = getCounterValueCode(state)
startSyncCounterValues(counterValue, accounts) startSyncCounterValues(counterValue, accounts)
}, SYNC_COUNTER_VALUES_DELAY) }, SYNC_COUNTER_VALUES_DELAY)
}, },
@ -245,7 +242,7 @@ export default ({ store, locked }: { store: Object, locked: boolean }) => {
if (!locked) { if (!locked) {
const accounts = getAccounts(state) const accounts = getAccounts(state)
const counterValue = getCounterValue(state) const counterValue = getCounterValueCode(state)
startSyncCounterValues(counterValue, accounts) startSyncCounterValues(counterValue, accounts)

1
src/renderer/index.js

@ -1,3 +1,4 @@
require('babel-polyfill')
const Raven = require('raven-js') const Raven = require('raven-js')
require('../env') require('../env')

6
yarn.lock

@ -955,9 +955,9 @@
dependencies: dependencies:
events "^2.0.0" events "^2.0.0"
"@ledgerhq/wallet-common@^0.10.0": "@ledgerhq/wallet-common@^0.10.1":
version "0.10.0" version "0.10.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-0.10.0.tgz#0275de0d0f73bdfb7aee7bbbaa324f2f3f993aee" resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-0.10.1.tgz#72e17329de02bebb5ed54bf797113266824e4af8"
dependencies: dependencies:
"@ledgerhq/currencies" "^4.7.1" "@ledgerhq/currencies" "^4.7.1"
axios "^0.18.0" axios "^0.18.0"

Loading…
Cancel
Save