diff --git a/package.json b/package.json index 0708592e..afcb1a3b 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "@ledgerhq/hw-app-xrp": "^4.13.0", "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "^4.13.0", - "@ledgerhq/ledger-core": "2.0.0-rc.4", - "@ledgerhq/live-common": "^2.32.0", + "@ledgerhq/ledger-core": "2.0.0-rc.5", + "@ledgerhq/live-common": "^2.35.0", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", diff --git a/src/api/Ripple.js b/src/api/Ripple.js index 8f0d5fd4..9adbf924 100644 --- a/src/api/Ripple.js +++ b/src/api/Ripple.js @@ -40,6 +40,7 @@ export const formatAPICurrencyXRP = (amount: number) => { const value = formatCurrencyUnit(rippleUnit, amount, { showAllDigits: true, disableRounding: true, + useGrouping: false, }) return { currency: 'XRP', value } } diff --git a/src/bridge/BridgeSyncContext.js b/src/bridge/BridgeSyncContext.js index 99099fb0..0a1181c7 100644 --- a/src/bridge/BridgeSyncContext.js +++ b/src/bridge/BridgeSyncContext.js @@ -3,7 +3,6 @@ // it handles automatically re-calling synchronize // this is an even high abstraction than the bridge -import invariant from 'invariant' import logger from 'logger' import shuffle from 'lodash/shuffle' import { timeout } from 'rxjs/operators/timeout' @@ -67,7 +66,10 @@ class Provider extends Component { return } const account = this.props.accounts.find(a => a.id === accountId) - invariant(account, 'account not found') + if (!account) { + next() + return + } const bridge = getBridgeForCurrency(account.currency) diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 48972922..3c366449 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -1,5 +1,6 @@ // @flow import { Observable } from 'rxjs' +import logger from 'logger' import React from 'react' import FeesField from 'components/FeesField/EthereumKind' import AdvancedOptions from 'components/AdvancedOptions/EthereumKind' @@ -176,7 +177,7 @@ const EthereumBridge: WalletBridge = { index, { address, path: freshAddressPath, publicKey }, isStandard, - mandatoryCount, + shouldSkipEmpty, ): { account?: Account, complete?: boolean } { const balance = await api.getAccountBalance(address) if (finished) return { complete: true } @@ -213,7 +214,7 @@ const EthereumBridge: WalletBridge = { newAccountCount++ } - if (index < mandatoryCount) { + if (shouldSkipEmpty) { return {} } // NB for legacy addresses maybe we will continue at least for the first 10 addresses @@ -254,6 +255,8 @@ const EthereumBridge: WalletBridge = { const last = derivations[derivations.length - 1] for (const derivation of derivations) { const isStandard = last === derivation + let emptyCount = 0 + const mandatoryEmptyAccountSkip = derivation.mandatoryEmptyAccountSkip || 0 for (let index = 0; index < 255; index++) { const freshAddressPath = derivation({ currency, x: index, segwit: false }) const res = await getAddressCommand @@ -263,10 +266,18 @@ const EthereumBridge: WalletBridge = { index, res, isStandard, - // $FlowFixMe i know i know, not part of function - derivation.mandatoryCount || 0, + emptyCount < mandatoryEmptyAccountSkip, ) - if (r.account) o.next(r.account) + logger.log( + `scanning ${currency.id} at ${freshAddressPath}: ${res.address} resulted of ${ + r.account ? `Account with ${r.account.operations.length} txs` : 'no account' + }. ${r.complete ? 'ALL SCANNED' : ''}`, + ) + if (r.account) { + o.next(r.account) + } else { + emptyCount++ + } if (r.complete) { break } diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index 6ea7ff24..c899c342 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -1,8 +1,7 @@ // @flow import React from 'react' -import { Observable } from 'rxjs' -import LRU from 'lru-cache' import { map } from 'rxjs/operators' +import LRU from 'lru-cache' import type { Account } from '@ledgerhq/live-common/lib/types' import { decodeAccount, encodeAccount } from 'reducers/accounts' import FeesBitcoinKind from 'components/FeesField/BitcoinKind' @@ -110,58 +109,41 @@ const LibcoreBridge: WalletBridge = { }, synchronize: account => - Observable.create(o => { - // FIXME TODO: - // - when you implement addPendingOperation you also here need to: - // - if there were pendingOperations that are now in operations, remove them as well. - // - if there are pendingOperations that is older than a threshold (that depends on blockchain speed typically) - // then we probably should trash them out? it's a complex question for UI - ;(async () => { - try { - const rawAccount = encodeAccount(account) - const rawSyncedAccount = await libcoreSyncAccount.send({ rawAccount }).toPromise() - const syncedAccount = decodeAccount(rawSyncedAccount) - o.next(account => { - const accountOps = account.operations - const syncedOps = syncedAccount.operations - const patch: $Shape = { - id: syncedAccount.id, - freshAddress: syncedAccount.freshAddress, - freshAddressPath: syncedAccount.freshAddressPath, - balance: syncedAccount.balance, - blockHeight: syncedAccount.blockHeight, - lastSyncDate: new Date(), - } - - const hasChanged = - accountOps.length !== syncedOps.length || // size change, we do a full refresh for now... - (accountOps.length > 0 && - syncedOps.length > 0 && - (accountOps[0].accountId !== syncedOps[0].accountId || - accountOps[0].id !== syncedOps[0].id || // if same size, only check if the last item has changed. - accountOps[0].blockHeight !== syncedOps[0].blockHeight)) - - if (hasChanged) { - patch.operations = syncedAccount.operations - patch.pendingOperations = [] // For now, we assume a change will clean the pendings. - } - - return { - ...account, - ...patch, - } - }) - o.complete() - } catch (e) { - o.error(e) + libcoreSyncAccount.send({ rawAccount: encodeAccount(account) }).pipe( + map(rawSyncedAccount => { + const syncedAccount = decodeAccount(rawSyncedAccount) + return account => { + const accountOps = account.operations + const syncedOps = syncedAccount.operations + const patch: $Shape = { + id: syncedAccount.id, + freshAddress: syncedAccount.freshAddress, + freshAddressPath: syncedAccount.freshAddressPath, + balance: syncedAccount.balance, + blockHeight: syncedAccount.blockHeight, + lastSyncDate: new Date(), + } + + const hasChanged = + accountOps.length !== syncedOps.length || // size change, we do a full refresh for now... + (accountOps.length > 0 && + syncedOps.length > 0 && + (accountOps[0].accountId !== syncedOps[0].accountId || + accountOps[0].id !== syncedOps[0].id || // if same size, only check if the last item has changed. + accountOps[0].blockHeight !== syncedOps[0].blockHeight)) + + if (hasChanged) { + patch.operations = syncedAccount.operations + patch.pendingOperations = [] // For now, we assume a change will clean the pendings. + } + + return { + ...account, + ...patch, + } } - })() - return { - unsubscribe() { - // LibcoreBridge: unsub sync not currently implemented - }, - } - }), + }), + ), pullMoreOperations: () => Promise.reject(notImplemented), diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index 53c5f181..e6908fbb 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -68,6 +68,7 @@ async function signAndBroadcast({ a, t, deviceId, isCancelled, onSigned, onOpera } const instruction = { fee: formatAPICurrencyXRP(t.fee).value, + maxLedgerVersionOffset: 12, } const prepared = await api.preparePayment(a.freshAddress, payment, instruction) @@ -416,6 +417,7 @@ const RippleJSBridge: WalletBridge = { const [last] = operations const pendingOperations = a.pendingOperations.filter( o => + !operations.some(op => o.hash === op.hash) && last && last.transactionSequenceNumber && o.transactionSequenceNumber && diff --git a/src/components/Onboarding/OnboardingBreadcrumb.js b/src/components/Onboarding/OnboardingBreadcrumb.js index b1505aef..240adb61 100644 --- a/src/components/Onboarding/OnboardingBreadcrumb.js +++ b/src/components/Onboarding/OnboardingBreadcrumb.js @@ -51,7 +51,7 @@ function OnboardingBreadcrumb(props: Props) { return ( diff --git a/src/components/base/Chart/handleMouseEvents.js b/src/components/base/Chart/handleMouseEvents.js index b82c0624..adb4d9e7 100644 --- a/src/components/base/Chart/handleMouseEvents.js +++ b/src/components/base/Chart/handleMouseEvents.js @@ -65,7 +65,7 @@ export default function handleMouseEvents({ NODES.tooltip .style('transition', '100ms cubic-bezier(.61,1,.53,1) opacity') .style('opacity', 1) - .style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, 0, 0)`) + .style('left', `${Math.floor(MARGINS.left + x(d.parsedDate))}px`) NODES.focus.style('opacity', 1) NODES.xBar.style('opacity', 1) } @@ -102,7 +102,7 @@ export default function handleMouseEvents({ , ), ) - .style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, 0, 0)`) + .style('left', `${Math.floor(MARGINS.left + x(d.parsedDate))}px`) NODES.xBar .attr('x1', x(d.parsedDate)) .attr('x2', x(d.parsedDate)) diff --git a/src/components/modals/Receive/steps/03-step-confirm-address.js b/src/components/modals/Receive/steps/03-step-confirm-address.js index 4e496999..6457c7d0 100644 --- a/src/components/modals/Receive/steps/03-step-confirm-address.js +++ b/src/components/modals/Receive/steps/03-step-confirm-address.js @@ -1,6 +1,5 @@ // @flow -import invariant from 'invariant' import styled from 'styled-components' import React, { Fragment, PureComponent } from 'react' @@ -13,9 +12,7 @@ import TranslatedError from '../../../TranslatedError' export default class StepConfirmAddress extends PureComponent { render() { - const { t, device, account, isAddressVerified, verifyAddressError, transitionTo } = this.props - invariant(account, 'No account given') - invariant(device, 'No device given') + const { t, account, isAddressVerified, verifyAddressError, transitionTo } = this.props return ( @@ -34,7 +31,8 @@ export default class StepConfirmAddress extends PureComponent { {t('app:receive.steps.confirmAddress.action')} - {t('app:receive.steps.confirmAddress.text', { currencyName: account.currency.name })} + {account && + t('app:receive.steps.confirmAddress.text', { currencyName: account.currency.name })}