diff --git a/.circleci/config.yml b/.circleci/config.yml index 1390a86a..c9faa4ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,13 +10,20 @@ jobs: <<: *defaults steps: - run: sudo apt-get install -y libudev-dev + - run: + name: Install latest yarn + command: | + curl -sS http://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - ; + echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list ; + sudo apt-get update && sudo apt-get install yarn + sudo rm /usr/local/bin/yarn # remove docker yarn - checkout - restore_cache: keys: - - v10-yarn-packages-{{ checksum "yarn.lock" }} + - v11-yarn-packages-{{ checksum "yarn.lock" }} - run: yarn install - save_cache: - key: v10-yarn-packages-{{ checksum "yarn.lock" }} + key: v11-yarn-packages-{{ checksum "yarn.lock" }} paths: - node_modules - run: yarn lint diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 9a32a5fb..1bf88ef7 100755 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,6 @@ --- name: ✨ Feature Request -about: Any feature you find missing in Ledger Live? Discuss to suggest feature requests. +about: Any feature you find missing in Ledger Live? Discuss to suggest feature requests. For crypto asset support, please read Issue 1650. --- - [ ] I have checked this feature was not yet requested. diff --git a/babel.config.js b/babel.config.js index 9b192822..27e3d5c3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,32 +1,62 @@ -const { NODE_ENV } = process.env +const { NODE_ENV, CLI } = process.env const __TEST__ = NODE_ENV === 'test' +const __CLI__ = !!CLI -module.exports = () => ({ - presets: [ - [ - require('@babel/preset-env'), - { - loose: true, - modules: __TEST__ ? 'commonjs' : false, - targets: { - electron: '1.8', - node: 'current', +module.exports = (api) => { + + if (api) { + api.cache(true); + } + + return { + presets: [ + [ + require('@babel/preset-env'), + { + loose: true, + modules: __TEST__ || __CLI__ ? 'commonjs' : false, + targets: { + electron: '1.8', + node: 'current', + }, }, - }, + ], + require('@babel/preset-flow'), + require('@babel/preset-react'), ], - require('@babel/preset-flow'), - require('@babel/preset-react'), - require('@babel/preset-stage-0'), - ], - plugins: [ - [require('babel-plugin-module-resolver'), { root: ['src'] }], - [ - require('babel-plugin-styled-components'), - { - displayName: true, - ssr: __TEST__, - }, + plugins: [ + [require('babel-plugin-module-resolver'), { root: ['src'] }], + [ + require('babel-plugin-styled-components'), + { + displayName: true, + ssr: __TEST__, + }, + ], + // Stage 0 + "@babel/plugin-proposal-function-bind", + + // Stage 1 + "@babel/plugin-proposal-export-default-from", + "@babel/plugin-proposal-logical-assignment-operators", + ["@babel/plugin-proposal-optional-chaining", { "loose": false }], + ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }], + ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], + "@babel/plugin-proposal-do-expressions", + + // Stage 2 + ["@babel/plugin-proposal-decorators", { "legacy": true }], + "@babel/plugin-proposal-function-sent", + "@babel/plugin-proposal-export-namespace-from", + "@babel/plugin-proposal-numeric-separator", + "@babel/plugin-proposal-throw-expressions", + + // Stage 3 + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-syntax-import-meta", + ["@babel/plugin-proposal-class-properties", { "loose": false }], + "@babel/plugin-proposal-json-strings" ], - ], -}) + } +} diff --git a/package.json b/package.json index 967b6537..b1263029 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dist": "bash ./scripts/dist.sh", "dist:dir": "bash ./scripts/dist-dir.sh", "compile": "bash ./scripts/compile.sh", + "cli": "bash ./scripts/cli/cli.sh", "lint": "eslint src webpack .storybook test-e2e", "flow": "flow", "test": "jest src", @@ -34,13 +35,13 @@ } }, "dependencies": { - "@ledgerhq/hw-app-btc": "4.21.0", - "@ledgerhq/hw-app-eth": "^4.14.0", - "@ledgerhq/hw-app-xrp": "^4.13.0", - "@ledgerhq/hw-transport": "^4.13.0", - "@ledgerhq/hw-transport-node-hid": "4.22.0", - "@ledgerhq/ledger-core": "2.0.0-rc.7", - "@ledgerhq/live-common": "^3.7.1", + "@ledgerhq/hw-app-btc": "4.24.0", + "@ledgerhq/hw-app-eth": "^4.24.0", + "@ledgerhq/hw-app-xrp": "^4.24.0", + "@ledgerhq/hw-transport": "^4.24.0", + "@ledgerhq/hw-transport-node-hid": "4.24.0", + "@ledgerhq/ledger-core": "2.0.0-rc.8", + "@ledgerhq/live-common": "4.0.0-beta.1", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", @@ -69,6 +70,7 @@ "measure-scrollbar": "^1.1.0", "moment": "^2.22.2", "qrcode": "^1.2.0", + "qrloop": "^0.6.1", "qs": "^6.5.1", "raven": "^2.5.0", "raven-js": "^3.24.2", @@ -114,12 +116,28 @@ "zxcvbn": "^4.4.2" }, "devDependencies": { - "@babel/core": "7.0.0-beta.42", - "@babel/polyfill": "7.0.0-beta.42", - "@babel/preset-env": "7.0.0-beta.42", - "@babel/preset-flow": "7.0.0-beta.42", - "@babel/preset-react": "7.0.0-beta.42", - "@babel/preset-stage-0": "7.0.0-beta.42", + "@babel/core": "7.1.2", + "@babel/plugin-proposal-class-properties": "7.1.0", + "@babel/plugin-proposal-decorators": "7.1.2", + "@babel/plugin-proposal-do-expressions": "7.0.0", + "@babel/plugin-proposal-export-default-from": "7.0.0", + "@babel/plugin-proposal-export-namespace-from": "7.0.0", + "@babel/plugin-proposal-function-bind": "7.0.0", + "@babel/plugin-proposal-function-sent": "7.1.0", + "@babel/plugin-proposal-json-strings": "7.0.0", + "@babel/plugin-proposal-logical-assignment-operators": "7.0.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.0.0", + "@babel/plugin-proposal-numeric-separator": "7.0.0", + "@babel/plugin-proposal-optional-chaining": "7.0.0", + "@babel/plugin-proposal-pipeline-operator": "7.0.0", + "@babel/plugin-proposal-throw-expressions": "7.0.0", + "@babel/plugin-syntax-dynamic-import": "7.0.0", + "@babel/plugin-syntax-import-meta": "7.0.0", + "@babel/polyfill": "7.0.0", + "@babel/preset-env": "7.1.0", + "@babel/preset-flow": "7.0.0", + "@babel/preset-react": "7.0.0", + "@babel/register": "7.0.0", "@octokit/rest": "^15.10.0", "@storybook/addon-actions": "^3.4.7", "@storybook/addon-knobs": "^3.4.7", @@ -167,5 +185,9 @@ "webpack-cli": "^2.0.14", "yaml-loader": "^0.5.0" }, + "engines": { + "node": ">=8.9.0 <=8.12.0", + "yarn": "^1.10.1" + }, "private": true } diff --git a/scripts/check-wordings.js b/scripts/check-wordings.js index e1f301cb..43adaa10 100644 --- a/scripts/check-wordings.js +++ b/scripts/check-wordings.js @@ -5,34 +5,34 @@ const { spawn } = require('child_process') // those wordings are dynamically created, so they are detected // as false positive const WHITELIST = [ - 'app:operation.type.IN', - 'app:operation.type.OUT', - 'app:exchange.coinhouse', - 'app:exchange.changelly', - 'app:exchange.coinmama', - 'app:exchange.simplex', - 'app:exchange.paybis', - 'app:addAccounts.accountToImportSubtitle_plural', - 'app:dashboard.summary_plural', - 'app:addAccounts.success_plural', - 'app:addAccounts.successDescription_plural', - 'app:time.since.day', - 'app:time.since.week', - 'app:time.since.month', - 'app:time.since.year', - 'app:time.day', - 'app:time.week', - 'app:time.month', - 'app:time.year', - 'app:addAccounts.cta.add_plural', - 'app:manager.apps.installing', - 'app:manager.apps.uninstalling', - 'app:manager.apps.installSuccess', - 'app:manager.apps.uninstallSuccess', + 'operation.type.IN', + 'operation.type.OUT', + 'exchange.coinhouse', + 'exchange.changelly', + 'exchange.coinmama', + 'exchange.simplex', + 'exchange.paybis', + 'addAccounts.accountToImportSubtitle_plural', + 'dashboard.summary_plural', + 'addAccounts.success_plural', + 'addAccounts.successDescription_plural', + 'time.since.day', + 'time.since.week', + 'time.since.month', + 'time.since.year', + 'time.day', + 'time.week', + 'time.month', + 'time.year', + 'addAccounts.cta.add_plural', + 'manager.apps.installing', + 'manager.apps.uninstalling', + 'manager.apps.installSuccess', + 'manager.apps.uninstallSuccess', ] const WORDINGS = { - app: require('../static/i18n/en/app.json'), + require('../static/i18n/en/app.json'), onboarding: require('../static/i18n/en/onboarding.json'), // errors: require('../static/i18n/en/errors.json'), // language: require('../static/i18n/en/language.json'), diff --git a/scripts/cli/cli.sh b/scripts/cli/cli.sh new file mode 100644 index 00000000..3484d589 --- /dev/null +++ b/scripts/cli/cli.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# TODO: os specific +export LEDGER_DATA_DIR="$HOME/.config/Electron" +export LEDGER_LOGS_DIRECTORY="$LEDGER_DATA_DIR/logs" +export LEDGER_LIVE_SQLITE_PATH="$LEDGER_DATA_DIR/sqlite" +export CLI=1 + +node -r @babel/register scripts/cli/txBetweenAccounts.js diff --git a/scripts/cli/getDevice.js b/scripts/cli/getDevice.js new file mode 100644 index 00000000..4489db4d --- /dev/null +++ b/scripts/cli/getDevice.js @@ -0,0 +1,21 @@ +import CommNodeHid from '@ledgerhq/hw-transport-node-hid' + +export default function getDevice() { + return new Promise((resolve, reject) => { + const sub = CommNodeHid.listen({ + error: err => { + sub.unsubscribe() + reject(err) + }, + next: async e => { + if (!e.device) { + return + } + if (e.type === 'add') { + sub.unsubscribe() + resolve(e.device) + } + }, + }) + }) +} diff --git a/scripts/cli/txBetweenAccounts.js b/scripts/cli/txBetweenAccounts.js new file mode 100644 index 00000000..bc7522ff --- /dev/null +++ b/scripts/cli/txBetweenAccounts.js @@ -0,0 +1,106 @@ +/* eslint-disable no-console */ + +import chalk from 'chalk' +import path from 'path' +import fs from 'fs' +import inquirer from 'inquirer' +import { createAccountModel } from '@ledgerhq/live-common/lib/models/account' +import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies' + +import 'globals' +import withLibcore from 'helpers/withLibcore' +import { doSignAndBroadcast } from 'commands/libcoreSignAndBroadcast' + +import getDevice from './getDevice' + +const accountModel = createAccountModel() + +async function main() { + try { + // GET ACCOUNTS + const app = await parseAppFile() + const accounts = app.accounts.map(accountModel.decode) + + // GET SENDER ACCOUNT + const senderAccount = await chooseAccount(accounts, 'Choose sender account') + + // GET RECIPIENT ACCOUNT + const recipientAccount = await chooseAccount(accounts, 'Choose recipient account') + + // GET AMOUNT & FEE + const { amount, feePerByte } = await inquirer.prompt([ + { + type: 'input', + name: 'amount', + message: 'Amount', + default: 0, + }, + { + type: 'input', + name: 'feePerByte', + message: 'Fee per byte', + default: 0, + }, + ]) + + // GET DEVICE + console.log(chalk.blue(`Waiting for device...`)) + const device = await getDevice() + console.log(chalk.blue(`Using device with path [${device.path}]`)) + + await withLibcore(async core => + doSignAndBroadcast({ + accountId: senderAccount.id, + currencyId: senderAccount.currency.id, + xpub: senderAccount.xpub, + freshAddress: senderAccount.freshAddress, + freshAddressPath: senderAccount.freshAddressPath, + index: senderAccount.index, + transaction: { + amount, + feePerByte, + recipient: recipientAccount.freshAddress, + }, + deviceId: device.path, + core, + isCancelled: () => false, + onSigned: () => { + console.log(`>> signed`) + }, + onOperationBroadcasted: operation => { + console.log(`>> broadcasted`, operation) + }, + }), + ) + } catch (err) { + console.log(`[ERROR]`, err) + } +} + +async function parseAppFile() { + const appFilePath = path.resolve(process.env.LEDGER_DATA_DIR, 'app.json') + const appFileContent = fs.readFileSync(appFilePath, 'utf-8') + const parsedApp = JSON.parse(appFileContent) + return parsedApp.data +} + +async function chooseAccount(accounts, msg) { + const { account } = await inquirer.prompt([ + { + type: 'list', + choices: accounts.map(account => ({ + name: `${account.name} | ${chalk.green( + formatCurrencyUnit(account.unit, account.balance, { + showCode: true, + }), + )}`, + value: account, + })), + name: 'account', + message: msg, + }, + ]) + return account +} + +main() diff --git a/scripts/patch-appimage.sh b/scripts/patch-appimage.sh new file mode 100755 index 00000000..6ab9b87d --- /dev/null +++ b/scripts/patch-appimage.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Patch .AppImage to address libcore crash on some +# distributions, due to loading system libraries +# instead of embedded ones. +# +# see https://github.com/LedgerHQ/ledger-live-desktop/issues/1010 + +set -e + +BASE_URL=http://mirrors.kernel.org/ubuntu/pool/main/k/krb5 +PACKAGE_SUFFIX=-2build1_amd64.deb +TMP_DIR=$(mktemp -d) +LEDGER_LIVE_VERSION=$(grep version package.json | sed -E 's/.*: "(.*)",/\1/g') + +cp "dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage" "$TMP_DIR" +pushd "$TMP_DIR" + +declare -a LIBRARIES=( + "libgssapi-krb5-2_1.16" + "libk5crypto3_1.16" + "libkrb5-3_1.16" + "libkrb5support0_1.16" +) + +for PACKAGE in "${LIBRARIES[@]}"; do + curl -fOL "$BASE_URL/$PACKAGE$PACKAGE_SUFFIX" + ar p "$PACKAGE$PACKAGE_SUFFIX" data.tar.xz | tar xvJf >/dev/null - ./usr/lib/x86_64-linux-gnu/ + rm "$PACKAGE$PACKAGE_SUFFIX" +done + +curl -fOL "https://s3-eu-west-1.amazonaws.com/ledger-ledgerlive-resources-dev/public_resources/appimagetool-x86_64.AppImage" + +cp "$OLDPWD/scripts/shasums/patch-appimage-sums.txt" . +sha512sum --quiet --check patch-appimage-sums.txt || exit 1 + +./ledger-live-desktop-"$LEDGER_LIVE_VERSION"-linux-x86_64.AppImage --appimage-extract +cp -a usr/lib/x86_64-linux-gnu/*.so.* squashfs-root/usr/lib + +chmod +x appimagetool-x86_64.AppImage +./appimagetool-x86_64.AppImage squashfs-root "$OLDPWD/dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage" + +popd + +MD5_SUM=$(sha512sum "dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage" | cut -f1 -d\ | xxd -r -p | base64 | paste -sd "") +sed -i "s|sha512: .*|sha512: ${MD5_SUM}|g" dist/latest-linux.yml + +SIZE=$(stat --printf="%s" "dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage") +sed -i "s|size: .*|size: ${SIZE}|g" dist/latest-linux.yml diff --git a/scripts/release.sh b/scripts/release.sh index a0912e45..14a573e9 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -13,7 +13,9 @@ if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then exit 0 fi -if ! git describe --exact-match --tags 2>/dev/null >/dev/null; then +GH_TAG=$(git describe --exact-match --tags 2>/dev/null || echo '') + +if [[ $GH_TAG == "" ]]; then echo "You are not on a tag. Exiting properly. (CI)" exit 0 fi @@ -57,7 +59,6 @@ fi # exit 1 # fi - if [[ $(uname) == 'Linux' ]]; then # only run it on one target, to prevent race conditions runJob \ "node scripts/create-draft-release.js" \ @@ -68,9 +69,48 @@ fi runJob "yarn compile" "compiling..." "compiled" "failed to compile" "verbose" +# -------------------------------------------------------------------- +# Linux: Internal process error (null) +# +# context: https://github.com/LedgerHQ/ledger-live-desktop/issues/1010 +# Linux: Internal process error (null) +# +# The "fix" is not optimal, as it doesn't really solve the problem +# (electron loading system openssl before we can load our embedded one) +# Quick summary: +# +# - build without publishing +# - unpack the .AppImage +# - download reported working libs from ubuntu mirrors, put it inside +# - re-pack the .AppImage +# - checksum stuff +# - upload to gh + runJob \ - "DEBUG=electron-builder electron-builder build --publish always" \ - "building, packaging and publishing app..." \ - "app built, packaged and published successfully" \ + "DEBUG=electron-builder electron-builder build --publish never" \ + "building and packaging app..." \ + "app built and packaged successfully" \ "failed to build app" \ "verbose" + +runJob \ + "scripts/patch-appimage.sh" \ + "patching AppImage..." \ + "AppImage patched successfully" \ + "failed to patch AppImage" + +LEDGER_LIVE_VERSION=$(grep version package.json | sed -E 's/.*: "(.*)",/\1/g') + +scripts/upload-github-release-asset.sh \ + github_api_token="$GH_TOKEN" \ + owner=LedgerHQ \ + repo=ledger-live-desktop \ + tag="$GH_TAG" \ + filename="dist/ledger-live-desktop-$LEDGER_LIVE_VERSION-linux-x86_64.AppImage" + +scripts/upload-github-release-asset.sh \ + github_api_token="$GH_TOKEN" \ + owner=LedgerHQ \ + repo=ledger-live-desktop \ + tag="$GH_TAG" \ + filename="dist/latest-linux.yml" diff --git a/scripts/shasums/patch-appimage-sums.txt b/scripts/shasums/patch-appimage-sums.txt new file mode 100644 index 00000000..e8b98576 --- /dev/null +++ b/scripts/shasums/patch-appimage-sums.txt @@ -0,0 +1,5 @@ +bebb42401a43971cfe3e31f2c9ee4efee352ce0d29a8ccc95ca1356a58463afd4876b133d9f4295697f96b76eb21b50c1909a073db753569e8969065eb40b306 usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2.2 +d7d2b38a46d65a06560241b226f61d81c4df28d56c6841dd34bb428802ace0fc80cf94de1e5117f0b85b2c69b550df61ac999184d5cfe8ecd3bea4d8394d1d21 usr/lib/x86_64-linux-gnu/libkrb5.so.3.3 +b025b755eb9a64f0d03a8e92c9e4b4f95c2c506bf070cf037841ef8cdb9013e16390d0e17330f2ce8c98c3b1f05b917a3018109acfde7aab50bc9d9fa70ea12b usr/lib/x86_64-linux-gnu/libk5crypto.so.3.1 +f181e41f306819c10054ff8ceebf4943858f2cd34dea5206b51141877e2f651be3c6435bb02538cbde2cc0415f38e476423a9fd6a428ca9d425e9c662483b9af usr/lib/x86_64-linux-gnu/libkrb5support.so.0.1 +dd8d81d4c1485209a65a1446225428a1b919478a74fd5698aff64cb8a67992544e62455f849ad73392505707cb94739de00af5ab340a22a87bb752c3808a55d2 appimagetool-x86_64.AppImage diff --git a/scripts/upload-github-release-asset.sh b/scripts/upload-github-release-asset.sh new file mode 100755 index 00000000..935bec39 --- /dev/null +++ b/scripts/upload-github-release-asset.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# Author: Stefan Buck +# License: MIT +# https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 +# +# +# This script accepts the following parameters: +# +# * owner +# * repo +# * filename +# * github_api_token +# +# Script to upload a release asset using the GitHub API v3. +# +# Example: +# +# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground filename=./build.zip +# + +# Check dependencies. +set -e + +# Validate settings. +[ "$TRACE" ] && set -x + +# shellcheck disable=SC2124 +CONFIG=$@ + +for line in $CONFIG; do + eval "$line" +done + +# Define variables. +GH_API="https://api.github.com" + +# shellcheck disable=SC2154 +GH_REPO="$GH_API/repos/$owner/$repo" + +# shellcheck disable=SC2154 +AUTH="Authorization: token $github_api_token" + +# github_api_token=$GH_TOKEN owner=LedgerHQ repo=ledger-live-desktop tag=v1.2.2 filename=./dist/electron-builder-debug.yml +LATEST_RELEASE_ID=$(curl -sH "$AUTH" "$GH_API/repos/LedgerHQ/ledger-live-desktop/releases" | grep '"id":' | head -n 1 | sed -E 's/.*: (.*),/\1/') + +# Validate token. +curl -o /dev/null -sH "$AUTH" "$GH_REPO" || { echo "Error: Invalid repo, token or network issue!"; exit 1; } + +# Get ID of the asset based on given filename. +# shellcheck disable=SC2154 +[ "$LATEST_RELEASE_ID" ] || { echo "Error: Failed to get release id"; exit 1; } + +# Upload asset +echo "Uploading asset... " + +# Construct url +# shellcheck disable=SC2154 +GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$LATEST_RELEASE_ID/assets?name=$(basename "$filename")" + +curl --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" "$GH_ASSET" diff --git a/src/__mocks__/storybook-state.js b/src/__mocks__/storybook-state.js index 036894ec..675e36b7 100644 --- a/src/__mocks__/storybook-state.js +++ b/src/__mocks__/storybook-state.js @@ -2,7 +2,7 @@ import { genStoreState } from '@ledgerhq/live-common/lib/countervalues/mock' import { getCryptoCurrencyById, getFiatCurrencyByTicker, -} from '@ledgerhq/live-common/lib/helpers/currencies' +} from '@ledgerhq/live-common/lib/currencies' export default { countervalues: genStoreState([ diff --git a/src/actions/general.js b/src/actions/general.js index 35607cfd..1c984625 100644 --- a/src/actions/general.js +++ b/src/actions/general.js @@ -8,7 +8,7 @@ import { getOrderAccounts, } from 'reducers/settings' import { accountsSelector } from 'reducers/accounts' -import { sortAccounts } from 'helpers/accountOrdering' +import { sortAccounts } from '@ledgerhq/live-common/lib/account' const accountsBtcBalanceSelector = createSelector( accountsSelector, diff --git a/src/api/Ripple.js b/src/api/Ripple.js index 06f359f9..2c4be71a 100644 --- a/src/api/Ripple.js +++ b/src/api/Ripple.js @@ -6,7 +6,7 @@ import { parseCurrencyUnit, getCryptoCurrencyById, formatCurrencyUnit, -} from '@ledgerhq/live-common/lib/helpers/currencies' +} from '@ledgerhq/live-common/lib/currencies' const rippleUnit = getCryptoCurrencyById('ripple').units[0] diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 69be1675..9713910c 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -8,14 +8,22 @@ import AdvancedOptions from 'components/AdvancedOptions/EthereumKind' import throttle from 'lodash/throttle' import flatMap from 'lodash/flatMap' import uniqBy from 'lodash/uniqBy' +import { + getDerivationModesForCurrency, + getDerivationScheme, + runDerivationScheme, + getMandatoryEmptyAccountSkip, +} from '@ledgerhq/live-common/lib/derivation' +import { + getAccountPlaceholderName, + getNewAccountPlaceholderName, +} from '@ledgerhq/live-common/lib/account' import type { Account, Operation } from '@ledgerhq/live-common/lib/types' import eip55 from 'eip55' import { apiForCurrency } from 'api/Ethereum' import type { Tx } from 'api/Ethereum' -import { getDerivations } from 'helpers/derivations' import getAddressCommand from 'commands/getAddress' import signTransactionCommand from 'commands/signTransaction' -import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' import { NotEnoughBalance, FeeNotLoaded, ETHAddressNonEIP } from 'config/errors' import type { EditProps, WalletBridge } from './types' @@ -210,8 +218,8 @@ const EthereumBridge: WalletBridge = { async function stepAddress( index, - { address, path: freshAddressPath, publicKey }, - isStandard, + { address, path: freshAddressPath }, + derivationMode, shouldSkipEmpty, ): { account?: Account, complete?: boolean } { const balance = await api.getAccountBalance(address) @@ -222,19 +230,21 @@ const EthereumBridge: WalletBridge = { if (finished) return { complete: true } const freshAddress = address - const accountId = `ethereumjs:${currency.id}:${address}:${publicKey}` + const accountId = `ethereumjs:2:${currency.id}:${address}:${derivationMode}` if (txs.length === 0 && balance.isZero()) { // this is an empty account - if (isStandard) { + if (derivationMode === '') { + // is standard derivation if (newAccountCount === 0) { // first zero account will emit one account as opportunity to create a new account.. const account: $Exact = { id: accountId, - xpub: '', + seedIdentifier: freshAddress, freshAddress, freshAddressPath, - name: getNewAccountPlaceholderName(currency, index), + derivationMode, + name: getNewAccountPlaceholderName({ currency, index, derivationMode }), balance, blockHeight: currentBlock.height, index, @@ -258,10 +268,11 @@ const EthereumBridge: WalletBridge = { const account: $Exact = { id: accountId, - xpub: '', + seedIdentifier: freshAddress, freshAddress, freshAddressPath, - name: getAccountPlaceholderName(currency, index, !isStandard), + derivationMode, + name: getAccountPlaceholderName({ currency, index, derivationMode }), balance, blockHeight: currentBlock.height, index, @@ -288,21 +299,22 @@ const EthereumBridge: WalletBridge = { async function main() { try { - const derivations = getDerivations(currency) - const last = derivations[derivations.length - 1] - for (const derivation of derivations) { - const isStandard = last === derivation + const derivationModes = getDerivationModesForCurrency(currency) + for (const derivationMode of derivationModes) { let emptyCount = 0 - const mandatoryEmptyAccountSkip = derivation.mandatoryEmptyAccountSkip || 0 + const mandatoryEmptyAccountSkip = getMandatoryEmptyAccountSkip(derivationMode) + const derivationScheme = getDerivationScheme({ derivationMode, currency }) for (let index = 0; index < 255; index++) { - const freshAddressPath = derivation({ currency, x: index, segwit: false }) + const freshAddressPath = runDerivationScheme(derivationScheme, currency, { + account: index, + }) const res = await getAddressCommand .send({ currencyId: currency.id, devicePath: deviceId, path: freshAddressPath }) .toPromise() const r = await stepAddress( index, res, - isStandard, + derivationMode, emptyCount < mandatoryEmptyAccountSkip, ) logger.log( diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index fa86c0eb..4ad2bfc0 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -129,7 +129,9 @@ const LibcoreBridge: WalletBridge = { libcoreSyncAccount .send({ accountId: account.id, - freshAddressPath: account.freshAddressPath, + derivationMode: account.derivationMode, + xpub: account.xpub || '', + seedIdentifier: account.seedIdentifier, index: account.index, currencyId: account.currency.id, }) @@ -218,9 +220,9 @@ const LibcoreBridge: WalletBridge = { .send({ accountId: account.id, currencyId: account.currency.id, - xpub: account.xpub, - freshAddress: account.freshAddress, - freshAddressPath: account.freshAddressPath, + xpub: account.xpub || '', // FIXME only reason is to build the op id. we need to consider another id for making op id. + derivationMode: account.derivationMode, + seedIdentifier: account.seedIdentifier, index: account.index, transaction: serializeTransaction(transaction), deviceId, diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index 94337482..31744b63 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -7,7 +7,15 @@ import bs58check from 'ripple-bs58check' import { computeBinaryTransactionHash } from 'ripple-hashes' import throttle from 'lodash/throttle' import type { Account, Operation } from '@ledgerhq/live-common/lib/types' -import { getDerivations } from 'helpers/derivations' +import { + getDerivationModesForCurrency, + getDerivationScheme, + runDerivationScheme, +} from '@ledgerhq/live-common/lib/derivation' +import { + getAccountPlaceholderName, + getNewAccountPlaceholderName, +} from '@ledgerhq/live-common/lib/account' import getAddress from 'commands/getAddress' import signTransaction from 'commands/signTransaction' import { @@ -19,7 +27,6 @@ import { } from 'api/Ripple' import FeesRippleKind from 'components/FeesField/RippleKind' import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind' -import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' import { NotEnoughBalance, FeeNotLoaded, @@ -211,7 +218,7 @@ const txToOperation = (account: Account) => ({ } const op: $Exact = { - id, + id: `${account.id}-${id}-${type}`, hash: id, accountId: account.id, type, @@ -289,17 +296,19 @@ const RippleJSBridge: WalletBridge = { const minLedgerVersion = Number(ledgers[0]) const maxLedgerVersion = Number(ledgers[1]) - const derivations = getDerivations(currency) - for (const derivation of derivations) { - const legacy = derivation !== derivations[derivations.length - 1] + const derivationModes = getDerivationModesForCurrency(currency) + for (const derivationMode of derivationModes) { + const derivationScheme = getDerivationScheme({ derivationMode, currency }) for (let index = 0; index < 255; index++) { - const freshAddressPath = derivation({ currency, x: index, segwit: false }) - const { address, publicKey } = await await getAddress + const freshAddressPath = runDerivationScheme(derivationScheme, currency, { + account: index, + }) + const { address } = await await getAddress .send({ currencyId: currency.id, devicePath: deviceId, path: freshAddressPath }) .toPromise() if (finished) return - const accountId = `ripplejs:${currency.id}:${address}:${publicKey}` + const accountId = `ripplejs:2:${currency.id}:${address}:${derivationMode}` let info try { @@ -316,11 +325,12 @@ const RippleJSBridge: WalletBridge = { if (!info) { // account does not exist in Ripple server // we are generating a new account locally - if (!legacy) { + if (derivationMode === '') { o.next({ id: accountId, - xpub: '', - name: getNewAccountPlaceholderName(currency, index), + seedIdentifier: freshAddress, + derivationMode, + name: getNewAccountPlaceholderName({ currency, index, derivationMode }), freshAddress, freshAddressPath, balance: BigNumber(0), @@ -353,8 +363,9 @@ const RippleJSBridge: WalletBridge = { const account: $Exact = { id: accountId, - xpub: '', - name: getAccountPlaceholderName(currency, index, legacy), + seedIdentifier: freshAddress, + derivationMode, + name: getAccountPlaceholderName({ currency, index, derivationMode }), freshAddress, freshAddressPath, balance, diff --git a/src/bridge/makeMockBridge.js b/src/bridge/makeMockBridge.js index d266c234..93080000 100644 --- a/src/bridge/makeMockBridge.js +++ b/src/bridge/makeMockBridge.js @@ -6,11 +6,11 @@ import { genAddingOperationsInAccount, genOperation, } from '@ledgerhq/live-common/lib/mock/account' -import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation' +import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/operation' import Prando from 'prando' import { BigNumber } from 'bignumber.js' import type { Operation } from '@ledgerhq/live-common/lib/types' -import { validateNameEdition } from 'helpers/accountName' +import { validateNameEdition } from '@ledgerhq/live-common/lib/account' import { MOCK_DATA_SEED } from 'config/constants' import type { WalletBridge } from './types' diff --git a/src/commands/debugAppInfosForCurrency.js b/src/commands/debugAppInfosForCurrency.js index 25239a1b..1e13096a 100644 --- a/src/commands/debugAppInfosForCurrency.js +++ b/src/commands/debugAppInfosForCurrency.js @@ -1,6 +1,6 @@ // @flow -import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { createCommand, Command } from 'helpers/ipc' import { fromPromise } from 'rxjs/observable/fromPromise' import { withDevice } from 'helpers/deviceAccess' diff --git a/src/commands/getAddress.js b/src/commands/getAddress.js index d2a24636..801f33e7 100644 --- a/src/commands/getAddress.js +++ b/src/commands/getAddress.js @@ -1,6 +1,6 @@ // @flow -import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { createCommand, Command } from 'helpers/ipc' import { fromPromise } from 'rxjs/observable/fromPromise' import { withDevice } from 'helpers/deviceAccess' diff --git a/src/commands/index.js b/src/commands/index.js index fad516a6..1022021c 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -69,8 +69,11 @@ const all: Array> = [ uninstallApp, ] +export const commandsById = {} + all.forEach(cmd => { invariant(!all.some(c => c !== cmd && c.id === cmd.id), `duplicate command '${cmd.id}'`) + commandsById[cmd.id] = cmd }) export default all diff --git a/src/commands/libcoreGetFees.js b/src/commands/libcoreGetFees.js index 4b3c8d5b..ef66bcb2 100644 --- a/src/commands/libcoreGetFees.js +++ b/src/commands/libcoreGetFees.js @@ -4,17 +4,16 @@ import { Observable } from 'rxjs' import { BigNumber } from 'bignumber.js' import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' +import { getWalletName } from '@ledgerhq/live-common/lib/account' import type { Account } from '@ledgerhq/live-common/lib/types' -import * as accountIdHelper from 'helpers/accountId' import { isValidAddress, libcoreAmountToBigNumber, bigNumberToLibcoreAmount, getOrCreateWallet, } from 'helpers/libcore' -import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' import { InvalidAddress } from 'config/errors' -import { splittedCurrencies } from 'config/cryptocurrencies' type BitcoinLikeTransaction = { // TODO we rename this Transaction concept into transactionInput @@ -24,22 +23,20 @@ type BitcoinLikeTransaction = { } type Input = { - accountId: string, accountIndex: number, transaction: BitcoinLikeTransaction, currencyId: string, - isSegwit: boolean, - isUnsplit: boolean, + derivationMode: string, + seedIdentifier: string, } export const extractGetFeesInputFromAccount = (a: Account) => { const currencyId = a.currency.id return { - accountId: a.id, accountIndex: a.index, currencyId, - isSegwit: isSegwitPath(a.freshAddressPath), - isUnsplit: isUnsplitPath(a.freshAddressPath, splittedCurrencies[currencyId]), + derivationMode: a.derivationMode, + seedIdentifier: a.seedIdentifier, } } @@ -47,17 +44,21 @@ type Result = { totalFees: string } const cmd: Command = createCommand( 'libcoreGetFees', - ({ accountId, currencyId, isSegwit, isUnsplit, accountIndex, transaction }) => + ({ currencyId, derivationMode, seedIdentifier, accountIndex, transaction }) => Observable.create(o => { let unsubscribed = false const isCancelled = () => unsubscribed + const currency = getCryptoCurrencyById(currencyId) withLibcore(async core => { - const { walletName } = accountIdHelper.decode(accountId) + const walletName = getWalletName({ + currency, + derivationMode, + seedIdentifier, + }) const njsWallet = await getOrCreateWallet(core, walletName, { - currencyId, - isSegwit, - isUnsplit, + currency, + derivationMode, }) if (isCancelled()) return const njsAccount = await njsWallet.getAccount(accountIndex) diff --git a/src/commands/libcoreScanFromXPUB.js b/src/commands/libcoreScanFromXPUB.js index 1c595861..0444f501 100644 --- a/src/commands/libcoreScanFromXPUB.js +++ b/src/commands/libcoreScanFromXPUB.js @@ -10,18 +10,18 @@ import { scanAccountsFromXPUB } from 'helpers/libcore' type Input = { currencyId: string, xpub: string, - isSegwit: boolean, - isUnsplit: boolean, + derivationMode: string, + seedIdentifier: string, } type Result = AccountRaw const cmd: Command = createCommand( 'libcoreScanFromXPUB', - ({ currencyId, xpub, isSegwit, isUnsplit }) => + ({ currencyId, xpub, derivationMode, seedIdentifier }) => fromPromise( withLibcore(async core => - scanAccountsFromXPUB({ core, currencyId, xpub, isSegwit, isUnsplit }), + scanAccountsFromXPUB({ core, currencyId, xpub, derivationMode, seedIdentifier }), ), ), ) diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index ad81d05c..98cd8ff5 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -2,22 +2,21 @@ import logger from 'logger' import { BigNumber } from 'bignumber.js' -import type { OperationRaw } from '@ledgerhq/live-common/lib/types' import Btc from '@ledgerhq/hw-app-btc' import { Observable } from 'rxjs' -import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' -import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' +import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation' +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' +import type { OperationRaw, CryptoCurrency } from '@ledgerhq/live-common/lib/types' +import { getWalletName } from '@ledgerhq/live-common/lib/account' import { libcoreAmountToBigNumber, bigNumberToLibcoreAmount, getOrCreateWallet, } from 'helpers/libcore' -import { splittedCurrencies } from 'config/cryptocurrencies' import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' import { withDevice } from 'helpers/deviceAccess' -import * as accountIdHelper from 'helpers/accountId' type BitcoinLikeTransaction = { amount: string, @@ -28,9 +27,9 @@ type BitcoinLikeTransaction = { type Input = { accountId: string, currencyId: string, + derivationMode: string, + seedIdentifier: string, xpub: string, - freshAddress: string, - freshAddressPath: string, index: number, transaction: BitcoinLikeTransaction, deviceId: string, @@ -42,17 +41,18 @@ type Result = { type: 'signed' } | { type: 'broadcasted', operation: OperationRa const cmd: Command = createCommand( 'libcoreSignAndBroadcast', - ({ accountId, currencyId, xpub, freshAddress, freshAddressPath, index, transaction, deviceId }) => + ({ accountId, currencyId, derivationMode, seedIdentifier, xpub, index, transaction, deviceId }) => Observable.create(o => { let unsubscribed = false + const currency = getCryptoCurrencyById(currencyId) const isCancelled = () => unsubscribed withLibcore(core => doSignAndBroadcast({ accountId, - currencyId, + currency, + derivationMode, + seedIdentifier, xpub, - freshAddress, - freshAddressPath, index, transaction, deviceId, @@ -78,28 +78,26 @@ const cmd: Command = createCommand( async function signTransaction({ hwApp, - currencyId, + currency, transaction, + derivationMode, sigHashType, - supportsSegwit, - isSegwit, hasTimestamp, }: { hwApp: Btc, - currencyId: string, + currency: CryptoCurrency, transaction: *, + derivationMode: string, sigHashType: number, - supportsSegwit: boolean, - isSegwit: boolean, hasTimestamp: boolean, }) { const additionals = [] let expiryHeight - if (currencyId === 'bitcoin_cash' || currencyId === 'bitcoin_gold') additionals.push('bip143') - if (currencyId === 'zcash') expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00]) + if (currency.id === 'bitcoin_cash' || currency.id === 'bitcoin_gold') additionals.push('bip143') + if (currency.id === 'zcash') expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00]) const rawInputs = transaction.getInputs() - const hasExtraData = currencyId === 'zcash' + const hasExtraData = currency.id === 'zcash' const inputs = await Promise.all( rawInputs.map(async input => { @@ -107,7 +105,7 @@ async function signTransaction({ const hexPreviousTransaction = Buffer.from(rawPreviousTransaction).toString('hex') const previousTransaction = hwApp.splitTransaction( hexPreviousTransaction, - supportsSegwit, + currency.supportsSegwit, hasTimestamp, hasExtraData, ) @@ -156,7 +154,7 @@ async function signTransaction({ outputScriptHex, lockTime, sigHashType, - isSegwit, + isSegwitDerivationMode(derivationMode), initialTimestamp, additionals, expiryHeight, @@ -167,9 +165,10 @@ async function signTransaction({ export async function doSignAndBroadcast({ accountId, - currencyId, + derivationMode, + seedIdentifier, + currency, xpub, - freshAddressPath, index, transaction, deviceId, @@ -179,10 +178,10 @@ export async function doSignAndBroadcast({ onOperationBroadcasted, }: { accountId: string, - currencyId: string, + derivationMode: string, + seedIdentifier: string, + currency: CryptoCurrency, xpub: string, - freshAddress: string, - freshAddressPath: string, index: number, transaction: BitcoinLikeTransaction, deviceId: string, @@ -191,11 +190,9 @@ export async function doSignAndBroadcast({ onSigned: () => void, onOperationBroadcasted: (optimisticOp: $Exact) => void, }): Promise { - const { walletName } = accountIdHelper.decode(accountId) + const walletName = getWalletName({ currency, seedIdentifier, derivationMode }) - const isSegwit = isSegwitPath(freshAddressPath) - const isUnsplit = isUnsplitPath(freshAddressPath, splittedCurrencies[currencyId]) - const njsWallet = await getOrCreateWallet(core, walletName, { currencyId, isSegwit, isUnsplit }) + const njsWallet = await getOrCreateWallet(core, walletName, { currency, derivationMode }) if (isCancelled()) return const njsAccount = await njsWallet.getAccount(index) if (isCancelled()) return @@ -221,17 +218,14 @@ export async function doSignAndBroadcast({ const hasTimestamp = !!njsWalletCurrency.bitcoinLikeNetworkParameters.UsesTimestampedTransaction // TODO: const timestampDelay = njsWalletCurrency.bitcoinLikeNetworkParameters.TimestampDelay - const currency = getCryptoCurrencyById(currencyId) - const signedTransaction = await withDevice(deviceId)(async transport => signTransaction({ hwApp: new Btc(transport), - currencyId, + currency, transaction: builded, sigHashType: parseInt(sigHashType, 16), - supportsSegwit: !!currency.supportsSegwit, - isSegwit: isSegwitPath(freshAddressPath), hasTimestamp, + derivationMode, }), ) diff --git a/src/commands/libcoreSyncAccount.js b/src/commands/libcoreSyncAccount.js index 21104d20..802428fa 100644 --- a/src/commands/libcoreSyncAccount.js +++ b/src/commands/libcoreSyncAccount.js @@ -1,6 +1,7 @@ // @flow import type { AccountRaw } from '@ledgerhq/live-common/lib/types' +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { fromPromise } from 'rxjs/observable/fromPromise' import { createCommand, Command } from 'helpers/ipc' @@ -9,15 +10,24 @@ import withLibcore from 'helpers/withLibcore' type Input = { accountId: string, - freshAddressPath: string, currencyId: string, + xpub: string, + derivationMode: string, + seedIdentifier: string, index: number, } type Result = { rawAccount: AccountRaw, requiresCacheFlush: boolean } -const cmd: Command = createCommand('libcoreSyncAccount', accountInfos => - fromPromise(withLibcore(core => syncAccount({ ...accountInfos, core }))), +const cmd: Command = createCommand( + 'libcoreSyncAccount', + ({ currencyId, ...accountInfos }) => + fromPromise( + withLibcore(core => { + const currency = getCryptoCurrencyById(currencyId) + return syncAccount({ ...accountInfos, currency, core }) + }), + ), ) export default cmd diff --git a/src/components/AccountPage/AccountHeaderActions.js b/src/components/AccountPage/AccountHeaderActions.js index 9ae4abb7..cb3510dd 100644 --- a/src/components/AccountPage/AccountHeaderActions.js +++ b/src/components/AccountPage/AccountHeaderActions.js @@ -7,7 +7,7 @@ import { translate } from 'react-i18next' import styled from 'styled-components' import type { Account } from '@ledgerhq/live-common/lib/types' import Tooltip from 'components/base/Tooltip' -import isAccountEmpty from 'helpers/isAccountEmpty' +import { isAccountEmpty } from '@ledgerhq/live-common/lib/account' import { MODAL_SEND, MODAL_RECEIVE, MODAL_SETTINGS_ACCOUNT } from 'config/constants' @@ -67,19 +67,19 @@ class AccountHeaderActions extends PureComponent { ) : null} - t('app:account.settings.title')}> + t('account.settings.title')}> openModal(MODAL_SETTINGS_ACCOUNT, { account })}> diff --git a/src/components/AccountPage/EmptyStateAccount.js b/src/components/AccountPage/EmptyStateAccount.js index 79353be4..11cddb4b 100644 --- a/src/components/AccountPage/EmptyStateAccount.js +++ b/src/components/AccountPage/EmptyStateAccount.js @@ -40,9 +40,9 @@ class EmptyStateAccount extends PureComponent { height="89" /> - {t('app:account.emptyState.title')} + {t('account.emptyState.title')} - + {'Make sure the'} {account.currency.managerAppName} @@ -53,7 +53,7 @@ class EmptyStateAccount extends PureComponent { diff --git a/src/components/AccountPage/index.js b/src/components/AccountPage/index.js index 169aec71..f065da0e 100644 --- a/src/components/AccountPage/index.js +++ b/src/components/AccountPage/index.js @@ -8,7 +8,7 @@ import { Redirect } from 'react-router' import type { Currency, Account } from '@ledgerhq/live-common/lib/types' import type { T } from 'types/common' import { accountSelector } from 'reducers/accounts' -import isAccountEmpty from 'helpers/isAccountEmpty' +import { isAccountEmpty } from '@ledgerhq/live-common/lib/account' import { counterValueCurrencySelector, localeSelector, @@ -97,7 +97,7 @@ class AccountPage extends PureComponent { /> - + diff --git a/src/components/AdvancedOptions/BitcoinKind.js b/src/components/AdvancedOptions/BitcoinKind.js index d9220787..45eb8491 100644 --- a/src/components/AdvancedOptions/BitcoinKind.js +++ b/src/components/AdvancedOptions/BitcoinKind.js @@ -15,12 +15,12 @@ type Props = { } export default translate()(({ isRBF, onChangeRBF, t }: Props) => ( - + @@ -32,7 +32,7 @@ export default translate()(({ isRBF, onChangeRBF, t }: Props) => ( diff --git a/src/components/AdvancedOptions/EthereumKind.js b/src/components/AdvancedOptions/EthereumKind.js index 4a15f33c..2060ccdd 100644 --- a/src/components/AdvancedOptions/EthereumKind.js +++ b/src/components/AdvancedOptions/EthereumKind.js @@ -15,11 +15,11 @@ type Props = { } export default translate()(({ gasLimit, onChangeGasLimit, t }: Props) => ( - + diff --git a/src/components/AdvancedOptions/RippleKind.js b/src/components/AdvancedOptions/RippleKind.js index ff34faa7..088acd36 100644 --- a/src/components/AdvancedOptions/RippleKind.js +++ b/src/components/AdvancedOptions/RippleKind.js @@ -33,7 +33,7 @@ class RippleKind extends Component { diff --git a/src/components/BalanceSummary/BalanceInfos.js b/src/components/BalanceSummary/BalanceInfos.js index aadf2fca..ef0bf11f 100644 --- a/src/components/BalanceSummary/BalanceInfos.js +++ b/src/components/BalanceSummary/BalanceInfos.js @@ -56,11 +56,7 @@ export function BalanceSincePercent(props: BalanceSinceProps) { withIcon /> )} - {!isAvailable ? ( - - ) : ( - {t(`app:time.since.${since}`)} - )} + {!isAvailable ? : {t(`time.since.${since}`)}} ) } @@ -82,11 +78,7 @@ export function BalanceSinceDiff(props: Props) { withIcon /> )} - {!isAvailable ? ( - - ) : ( - {t(`app:time.since.${since}`)} - )} + {!isAvailable ? : {t(`time.since.${since}`)}} ) } @@ -127,7 +119,7 @@ function BalanceInfos(props: Props) { isAvailable={isAvailable} totalBalance={totalBalance} > - {t('app:dashboard.totalBalance')} + {t('dashboard.totalBalance')} { return ( } - label={t('app:common.copyAddress')} + label={t('common.copyAddress')} onClick={() => { this.setState({ copyFeedback: true }) this._timeout = setTimeout(() => this.setState({ copyFeedback: false }), 1e3) @@ -193,17 +193,17 @@ class CurrentAddress extends PureComponent {
- {copyFeedback && {t('app:common.addressCopied')}} + {copyFeedback && {t('common.addressCopied')}} {address}
@@ -217,19 +217,17 @@ class CurrentAddress extends PureComponent { ff="Open Sans" > {isAddressVerified === null - ? t('app:currentAddress.messageIfUnverified', { currencyName }) + ? t('currentAddress.messageIfUnverified', { currencyName }) : isAddressVerified - ? t('app:currentAddress.messageIfAccepted', { currencyName }) - : t('app:currentAddress.messageIfSkipped', { currencyName })} + ? t('currentAddress.messageIfAccepted', { currencyName }) + : t('currentAddress.messageIfSkipped', { currencyName })}