1
0
mirror of https://github.com/trezor/trezor-wallet synced 2025-01-03 21:00:55 +00:00

Merge branch 'master' into fix/overflow

This commit is contained in:
slowbackspace 2019-01-03 15:43:54 +01:00
commit 77feccaab2
42 changed files with 271 additions and 254 deletions

View File

@ -6,7 +6,6 @@
.*/node_modules/react-redux/.* .*/node_modules/react-redux/.*
.*/node_modules/redux/.* .*/node_modules/redux/.*
.*/node_modules/react-router/.* .*/node_modules/react-router/.*
.*/node_modules/react-router-redux/.*
.*/node_modules/oboe/test/.* .*/node_modules/oboe/test/.*
.*/_old/.* .*/_old/.*
.*/scripts/solidity/.* .*/scripts/solidity/.*
@ -17,7 +16,7 @@
./src/flowtype/npm/react-redux_v5.x.x.js ./src/flowtype/npm/react-redux_v5.x.x.js
./src/flowtype/npm/react-router_v4.x.x.js ./src/flowtype/npm/react-router_v4.x.x.js
./src/flowtype/npm/react-router-dom_v4.x.x.js ./src/flowtype/npm/react-router-dom_v4.x.x.js
./src/flowtype/npm/react-router-redux.js ./src/flowtype/npm/connected-react-router.js
./src/flowtype/npm/bignumber.js ./src/flowtype/npm/bignumber.js
./src/flowtype/npm/ethereum-types.js ./src/flowtype/npm/ethereum-types.js
./src/flowtype/npm/web3.js ./src/flowtype/npm/web3.js

View File

@ -1,8 +1,9 @@
image: node:9.3 image: node:9.3
cache: cache:
key: ${CI_COMMIT_REF_SLUG}
paths: paths:
- node_modules - node_modules/
stages: stages:
- test - test
@ -18,6 +19,7 @@ lint:
flow: flow:
stage: test stage: test
script: script:
- yarn install
- yarn run flow - yarn run flow
unit: unit:

View File

@ -30,6 +30,7 @@
"bignumber.js": "2.4.0", "bignumber.js": "2.4.0",
"color-hash": "^1.0.3", "color-hash": "^1.0.3",
"commander": "^2.19.0", "commander": "^2.19.0",
"connected-react-router": "^6.0.0",
"copy-webpack-plugin": "^4.5.2", "copy-webpack-plugin": "^4.5.2",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"date-fns": "^1.29.0", "date-fns": "^1.29.0",
@ -54,9 +55,9 @@
"react-hot-loader": "^4.6.2", "react-hot-loader": "^4.6.2",
"react-json-view": "^1.19.1", "react-json-view": "^1.19.1",
"react-qr-svg": "^2.1.0", "react-qr-svg": "^2.1.0",
"react-redux": "^5.0.7", "react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-router-redux": "next",
"react-scale-text": "^1.2.2", "react-scale-text": "^1.2.2",
"react-select": "2.0.0", "react-select": "2.0.0",
"react-textarea-autosize": "^7.0.4", "react-textarea-autosize": "^7.0.4",

View File

@ -4,59 +4,13 @@ import * as ACCOUNT from 'actions/constants/account';
import type { Action } from 'flowtype'; import type { Action } from 'flowtype';
import type { Account, State } from 'reducers/AccountsReducer'; import type { Account, State } from 'reducers/AccountsReducer';
export type AccountFromStorageAction = { export type AccountAction = {
type: typeof ACCOUNT.FROM_STORAGE, type: typeof ACCOUNT.FROM_STORAGE,
payload: State payload: State
} } | {
type: typeof ACCOUNT.CREATE | typeof ACCOUNT.UPDATE,
export type AccountCreateAction = {
type: typeof ACCOUNT.CREATE,
payload: Account, payload: Account,
} };
export type AccountUpdateAction = {
type: typeof ACCOUNT.UPDATE,
payload: Account,
}
export type AccountSetBalanceAction = {
type: typeof ACCOUNT.SET_BALANCE,
address: string,
network: string,
deviceState: string,
balance: string
}
export type AccountSetNonceAction = {
type: typeof ACCOUNT.SET_NONCE,
address: string,
network: string,
deviceState: string,
nonce: number
}
export type AccountAction =
AccountFromStorageAction
| AccountCreateAction
| AccountUpdateAction
| AccountSetBalanceAction
| AccountSetNonceAction;
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({
type: ACCOUNT.SET_BALANCE,
address,
network,
deviceState,
balance,
});
export const setNonce = (address: string, network: string, deviceState: string, nonce: number): Action => ({
type: ACCOUNT.SET_NONCE,
address,
network,
deviceState,
nonce,
});
export const update = (account: Account): Action => ({ export const update = (account: Account): Action => ({
type: ACCOUNT.UPDATE, type: ACCOUNT.UPDATE,

View File

@ -12,7 +12,7 @@ import { httpRequest } from 'utils/networkUtils';
import * as buildUtils from 'utils/build'; import * as buildUtils from 'utils/build';
import * as storageUtils from 'utils/storage'; import * as storageUtils from 'utils/storage';
import { findAccountTokens } from 'reducers/TokensReducer'; import { getAccountTokens } from 'reducers/utils';
import type { Account } from 'reducers/AccountsReducer'; import type { Account } from 'reducers/AccountsReducer';
import type { Token } from 'reducers/TokensReducer'; import type { Token } from 'reducers/TokensReducer';
import type { Discovery } from 'reducers/DiscoveryReducer'; import type { Discovery } from 'reducers/DiscoveryReducer';
@ -59,11 +59,11 @@ const KEY_BETA_MODAL: string = '/betaModalPrivacy'; // this key needs to be comp
const findAccounts = (devices: Array<TrezorDevice>, accounts: Array<Account>): Array<Account> => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []); const findAccounts = (devices: Array<TrezorDevice>, accounts: Array<Account>): Array<Account> => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []);
const findTokens = (accounts: Array<Account>, tokens: Array<Token>): Array<Token> => accounts.reduce((arr, account) => arr.concat(findAccountTokens(tokens, account)), []); const findTokens = (accounts: Array<Account>, tokens: Array<Token>): Array<Token> => accounts.reduce((arr, account) => arr.concat(getAccountTokens(tokens, account)), []);
const findDiscovery = (devices: Array<TrezorDevice>, discovery: Array<Discovery>): Array<Discovery> => devices.reduce((arr, dev) => arr.concat(discovery.filter(d => d.deviceState === dev.state && d.completed)), []); const findDiscovery = (devices: Array<TrezorDevice>, discovery: Array<Discovery>): Array<Discovery> => devices.reduce((arr, dev) => arr.concat(discovery.filter(d => d.deviceState === dev.state && d.completed)), []);
const findPendingTxs = (accounts: Array<Account>, pending: Array<Transaction>): Array<Transaction> => accounts.reduce((result, account) => result.concat(pending.filter(p => p.address === account.address && p.network === account.network)), []); const findPendingTxs = (accounts: Array<Account>, pending: Array<Transaction>): Array<Transaction> => accounts.reduce((result, account) => result.concat(pending.filter(p => p.address === account.descriptor && p.network === account.network)), []);
export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const devices: Array<TrezorDevice> = getState().devices.filter(d => d.features && d.remember === true); const devices: Array<TrezorDevice> = getState().devices.filter(d => d.features && d.remember === true);

View File

@ -1,8 +1,8 @@
/* @flow */ /* @flow */
import { push, LOCATION_CHANGE } from 'react-router-redux'; import { push, LOCATION_CHANGE } from 'connected-react-router';
import { CONTEXT_NONE } from 'actions/constants/modal'; import { CONTEXT_NONE } from 'actions/constants/modal';
import { SET_INITIAL_URL } from 'actions/constants/wallet'; import { SET_INITIAL_URL, SET_FIRST_LOCATION_CHANGE } from 'actions/constants/wallet';
import { routes } from 'support/routes'; import { routes } from 'support/routes';
import * as deviceUtils from 'utils/device'; import * as deviceUtils from 'utils/device';
@ -15,7 +15,7 @@ import type {
Dispatch, Dispatch,
GetState, GetState,
} from 'flowtype'; } from 'flowtype';
import type { RouterAction } from 'react-router-redux'; import type { RouterAction } from 'connected-react-router';
/* /*
* Parse url string to RouterLocationState object (key/value) * Parse url string to RouterLocationState object (key/value)
@ -51,6 +51,7 @@ export const pathToParams = (path: string): PayloadAction<RouterLocationState> =
*/ */
export const paramsValidation = (params: RouterLocationState): PayloadAction<boolean> => (dispatch: Dispatch, getState: GetState): boolean => { export const paramsValidation = (params: RouterLocationState): PayloadAction<boolean> => (dispatch: Dispatch, getState: GetState): boolean => {
// validate requested device // validate requested device
if (params.hasOwnProperty('device')) { if (params.hasOwnProperty('device')) {
const { devices } = getState(); const { devices } = getState();
@ -105,7 +106,6 @@ export const paramsToPath = (params: RouterLocationState): PayloadAction<?string
break; break;
} }
} }
// pattern not found, redirect back // pattern not found, redirect back
if (!patternToUse) return null; if (!patternToUse) return null;
@ -129,12 +129,17 @@ export const paramsToPath = (params: RouterLocationState): PayloadAction<?string
export const getValidUrl = (action: RouterAction): PayloadAction<string> => (dispatch: Dispatch, getState: GetState): string => { export const getValidUrl = (action: RouterAction): PayloadAction<string> => (dispatch: Dispatch, getState: GetState): string => {
const { location } = getState().router; const { location } = getState().router;
const { firstLocationChange } = getState().wallet;
// redirect to landing page (loading screen) // redirect to landing page (loading screen)
// and wait until application is ready // and wait until application is ready
if (!location) return '/'; if (firstLocationChange) {
dispatch({
type: SET_FIRST_LOCATION_CHANGE,
});
return '/';
}
const requestedUrl = action.payload.pathname; const requestedUrl = action.payload.location.pathname;
// Corner case: LOCATION_CHANGE was called but pathname didn't changed (redirect action from RouterService) // Corner case: LOCATION_CHANGE was called but pathname didn't changed (redirect action from RouterService)
if (requestedUrl === location.pathname) return requestedUrl; if (requestedUrl === location.pathname) return requestedUrl;
@ -351,10 +356,12 @@ export const setInitialUrl = (): PayloadAction<boolean> => (dispatch: Dispatch,
const valid = dispatch(getValidUrl({ const valid = dispatch(getValidUrl({
type: LOCATION_CHANGE, type: LOCATION_CHANGE,
payload: { payload: {
pathname: initialPathname, location: {
hash: '', pathname: initialPathname,
search: '', hash: '',
state: {}, search: '',
state: {},
},
}, },
})); }));

View File

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'connected-react-router';
import { BLOCKCHAIN } from 'trezor-connect'; import { BLOCKCHAIN } from 'trezor-connect';
import * as WALLET from 'actions/constants/wallet'; import * as WALLET from 'actions/constants/wallet';
import * as ACCOUNT from 'actions/constants/account'; import * as ACCOUNT from 'actions/constants/account';
@ -210,7 +210,7 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
const account = reducerUtils.getSelectedAccount(state); const account = reducerUtils.getSelectedAccount(state);
const network = reducerUtils.getSelectedNetwork(state); const network = reducerUtils.getSelectedNetwork(state);
const discovery = reducerUtils.getDiscoveryProcess(state); const discovery = reducerUtils.getDiscoveryProcess(state);
const tokens = reducerUtils.getAccountTokens(state, account); const tokens = reducerUtils.getAccountTokens(state.tokens, account);
const pending = reducerUtils.getAccountPendingTx(state.pending, account); const pending = reducerUtils.getAccountPendingTx(state.pending, account);
// prepare new state for "selectedAccount" reducer // prepare new state for "selectedAccount" reducer

View File

@ -1,6 +1,6 @@
/* @flow */ /* @flow */
import * as storageUtils from 'utils/storage'; import * as storageUtils from 'utils/storage';
import { findToken } from 'reducers/TokensReducer'; import { findToken } from 'reducers/utils';
import type { State as EthereumSendFormState } from 'reducers/SendFormEthereumReducer'; import type { State as EthereumSendFormState } from 'reducers/SendFormEthereumReducer';
import type { State as RippleSendFormState } from 'reducers/SendFormRippleReducer'; import type { State as RippleSendFormState } from 'reducers/SendFormRippleReducer';
@ -44,7 +44,7 @@ export const loadEthereumDraftTransaction = (): PayloadAction<?EthereumSendFormS
if (state.currency !== state.networkSymbol) { if (state.currency !== state.networkSymbol) {
const { account, tokens } = getState().selectedAccount; const { account, tokens } = getState().selectedAccount;
if (!account) return null; if (!account) return null;
const token = findToken(tokens, account.address, state.currency, account.deviceState); const token = findToken(tokens, account.descriptor, state.currency, account.deviceState);
if (!token) { if (!token) {
storageUtils.remove(TYPE, key); storageUtils.remove(TYPE, key);
return null; return null;

View File

@ -76,7 +76,7 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async
name: token.name, name: token.name,
symbol: token.symbol, symbol: token.symbol,
address: token.address, address: token.address,
ethAddress: account.address, ethAddress: account.descriptor,
decimals: token.decimals, decimals: token.decimals,
balance: '0', balance: '0',
}; };
@ -87,7 +87,7 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async
}); });
const tokenBalance = await dispatch(BlockchainActions.getTokenBalance(tkn)); const tokenBalance = await dispatch(BlockchainActions.getTokenBalance(tkn));
dispatch(setBalance(token.address, account.address, tokenBalance)); dispatch(setBalance(token.address, account.descriptor, tokenBalance));
}; };
export const remove = (token: Token): Action => ({ export const remove = (token: Token): Action => ({

View File

@ -1,6 +1,6 @@
/* @flow */ /* @flow */
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'connected-react-router';
import { DEVICE } from 'trezor-connect'; import { DEVICE } from 'trezor-connect';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
import * as WALLET from 'actions/constants/wallet'; import * as WALLET from 'actions/constants/wallet';
@ -39,7 +39,7 @@ export type WalletAction = {
type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA, type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
devices: Array<TrezorDevice> devices: Array<TrezorDevice>
} | { } | {
type: typeof WALLET.SHOW_BETA_DISCLAIMER | typeof WALLET.HIDE_BETA_DISCLAIMER, type: typeof WALLET.SHOW_BETA_DISCLAIMER | typeof WALLET.HIDE_BETA_DISCLAIMER | typeof WALLET.SET_FIRST_LOCATION_CHANGE,
} }
export const init = (): ThunkAction => (dispatch: Dispatch): void => { export const init = (): ThunkAction => (dispatch: Dispatch): void => {

View File

@ -193,9 +193,10 @@ export const getTxInput = (): PromiseAction<void> => async (dispatch: Dispatch):
export const updateAccount = (account: Account, newAccount: EthereumAccount, network: string): PromiseAction<void> => async (dispatch: Dispatch): Promise<void> => { export const updateAccount = (account: Account, newAccount: EthereumAccount, network: string): PromiseAction<void> => async (dispatch: Dispatch): Promise<void> => {
const instance: Web3Instance = await dispatch(initWeb3(network)); const instance: Web3Instance = await dispatch(initWeb3(network));
const balance = await instance.web3.eth.getBalance(account.address); const balance = await instance.web3.eth.getBalance(account.descriptor);
const nonce = await instance.web3.eth.getTransactionCount(account.address); const nonce = await instance.web3.eth.getTransactionCount(account.descriptor);
dispatch(AccountsActions.update({ dispatch(AccountsActions.update({
networkType: 'ethereum',
...account, ...account,
...newAccount, ...newAccount,
nonce, nonce,
@ -208,7 +209,7 @@ export const updateAccount = (account: Account, newAccount: EthereumAccount, net
}; };
export const updateAccountTokens = (account: Account): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const updateAccountTokens = (account: Account): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const tokens = getState().tokens.filter(t => t.network === account.network && t.ethAddress === account.address); const tokens = getState().tokens.filter(t => t.network === account.network && t.ethAddress === account.descriptor);
tokens.forEach(async (token) => { tokens.forEach(async (token) => {
const balance = await dispatch(getTokenBalance(token)); const balance = await dispatch(getTokenBalance(token));
// const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10); // const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);

View File

@ -2,6 +2,7 @@
export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown'; export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown';
export const SET_INITIAL_URL: 'wallet__set_initial_url' = 'wallet__set_initial_url'; export const SET_INITIAL_URL: 'wallet__set_initial_url' = 'wallet__set_initial_url';
export const SET_FIRST_LOCATION_CHANGE: 'wallet__set_first_location_change' = 'wallet__set_first_location_change';
export const ONLINE_STATUS: 'wallet__online_status' = 'wallet__online_status'; export const ONLINE_STATUS: 'wallet__online_status' = 'wallet__online_status';
export const SET_SELECTED_DEVICE: 'wallet__set_selected_device' = 'wallet__set_selected_device'; export const SET_SELECTED_DEVICE: 'wallet__set_selected_device' = 'wallet__set_selected_device';

View File

@ -85,7 +85,7 @@ export const estimateGasLimit = (network: string, data: string, value: string, g
}; };
export const subscribe = (network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const subscribe = (network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const accounts: Array<string> = getState().accounts.filter(a => a.network === network).map(a => a.address); // eslint-disable-line no-unused-vars const accounts: Array<string> = getState().accounts.filter(a => a.network === network).map(a => a.descriptor); // eslint-disable-line no-unused-vars
const response = await TrezorConnect.blockchainSubscribe({ const response = await TrezorConnect.blockchainSubscribe({
accounts, accounts,
coin: network, coin: network,
@ -129,7 +129,7 @@ export const onBlockMined = (network: string): PromiseAction<void> => async (dis
export const onNotification = (payload: $ElementType<BlockchainNotification, 'payload'>): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const onNotification = (payload: $ElementType<BlockchainNotification, 'payload'>): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { notification } = payload; const { notification } = payload;
const account = getState().accounts.find(a => a.address === notification.address); const account = getState().accounts.find(a => a.descriptor === notification.address);
if (!account) return; if (!account) return;
if (notification.status === 'pending') { if (notification.status === 'pending') {

View File

@ -8,13 +8,13 @@ import * as BlockchainActions from 'actions/ethereum/BlockchainActions';
import type { import type {
PromiseAction, PromiseAction,
Dispatch, Dispatch,
GetState,
TrezorDevice, TrezorDevice,
Network, Network,
Account, Account,
} from 'flowtype'; } from 'flowtype';
import type { Discovery } from 'reducers/DiscoveryReducer'; import type { Discovery } from 'reducers/DiscoveryReducer';
export type DiscoveryStartAction = { export type DiscoveryStartAction = {
type: typeof DISCOVERY.START, type: typeof DISCOVERY.START,
networkType: 'ethereum', networkType: 'ethereum',
@ -61,33 +61,38 @@ export const begin = (device: TrezorDevice, network: Network): PromiseAction<Dis
}; };
}; };
export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): PromiseAction<Account> => async (dispatch: Dispatch): Promise<Account> => { export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): PromiseAction<Account> => async (dispatch: Dispatch, getState: GetState): Promise<Account> => {
const { config } = getState().localStorage;
const network = config.networks.find(c => c.shortcut === discoveryProcess.network);
if (!network) throw new Error('Discovery network not found');
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`); const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex); const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex'); const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress); const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const { network } = discoveryProcess;
// TODO: check if address was created before // TODO: check if address was created before
const account = await dispatch(BlockchainActions.discoverAccount(device, ethAddress, network)); const account = await dispatch(BlockchainActions.discoverAccount(device, ethAddress, network.shortcut));
// const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0'; // const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0';
const empty = account.nonce <= 0 && account.balance === '0'; const empty = account.nonce <= 0 && account.balance === '0';
return { return {
imported: false,
index: discoveryProcess.accountIndex, index: discoveryProcess.accountIndex,
loaded: true, network: network.shortcut,
network,
deviceID: device.features ? device.features.device_id : '0', deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0', deviceState: device.state || '0',
addressPath: path, accountPath: path,
address: ethAddress, descriptor: ethAddress,
balance: account.balance, balance: account.balance,
availableBalance: account.balance, availableBalance: account.balance,
sequence: account.nonce,
nonce: account.nonce,
block: account.block, block: account.block,
transactions: account.transactions, transactions: account.transactions,
empty, empty,
networkType: 'ethereum',
nonce: account.nonce,
}; };
}; };

View File

@ -8,7 +8,6 @@ import * as NOTIFICATION from 'actions/constants/notification';
import * as SEND from 'actions/constants/send'; import * as SEND from 'actions/constants/send';
import * as WEB3 from 'actions/constants/web3'; import * as WEB3 from 'actions/constants/web3';
import { initialState } from 'reducers/SendFormEthereumReducer'; import { initialState } from 'reducers/SendFormEthereumReducer';
import { findToken } from 'reducers/TokensReducer';
import * as reducerUtils from 'reducers/utils'; import * as reducerUtils from 'reducers/utils';
import * as ethUtils from 'utils/ethUtils'; import * as ethUtils from 'utils/ethUtils';
@ -76,7 +75,7 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction =
// make sure that this token is added into account // make sure that this token is added into account
const { account, tokens } = getState().selectedAccount; const { account, tokens } = getState().selectedAccount;
if (!account) return; if (!account) return;
const token = findToken(tokens, account.address, currentState.sendFormEthereum.currency, account.deviceState); const token = reducerUtils.findToken(tokens, account.descriptor, currentState.sendFormEthereum.currency, account.deviceState);
if (!token) { if (!token) {
// token not found, re-init form // token not found, re-init form
dispatch(init()); dispatch(init());
@ -456,7 +455,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
pending, pending,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !network) return; if (!account || account.networkType !== 'ethereum' || !network) return;
const currentState: State = getState().sendFormEthereum; const currentState: State = getState().sendFormEthereum;
@ -466,8 +465,8 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
const txData = await dispatch(prepareEthereumTx({ const txData = await dispatch(prepareEthereumTx({
network: network.shortcut, network: network.shortcut,
token: isToken ? findToken(getState().tokens, account.address, currentState.currency, account.deviceState) : null, token: isToken ? reducerUtils.findToken(getState().tokens, account.descriptor, currentState.currency, account.deviceState) : null,
from: account.address, from: account.descriptor,
to: currentState.address, to: currentState.address,
amount: currentState.amount, amount: currentState.amount,
data: currentState.data, data: currentState.data,
@ -487,7 +486,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
}, },
// useEmptyPassphrase: !selected.instance, // useEmptyPassphrase: !selected.instance,
useEmptyPassphrase: selected.useEmptyPassphrase, useEmptyPassphrase: selected.useEmptyPassphrase,
path: account.addressPath, path: account.accountPath,
transaction: txData, transaction: txData,
}); });
@ -533,10 +532,10 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
type: 'send', type: 'send',
status: 'pending', status: 'pending',
confirmations: 0, confirmations: 0,
address: account.address, address: account.descriptor,
inputs: [ inputs: [
{ {
addresses: [account.address], addresses: [account.descriptor],
amount: currentState.amount, amount: currentState.amount,
fee, fee,
total: currentState.total, total: currentState.total,

View File

@ -3,8 +3,7 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsUnits from 'ethereumjs-units'; import EthereumjsUnits from 'ethereumjs-units';
import { findToken } from 'reducers/TokensReducer'; import { findDevice, getPendingAmount, findToken } from 'reducers/utils';
import { findDevice, getPendingAmount } from 'reducers/utils';
import * as SEND from 'actions/constants/send'; import * as SEND from 'actions/constants/send';
import * as ethUtils from 'utils/ethUtils'; import * as ethUtils from 'utils/ethUtils';
@ -114,7 +113,7 @@ export const recalculateTotalAmount = ($state: State): PayloadAction<State> => (
if (state.setMax) { if (state.setMax) {
const pendingAmount = getPendingAmount(pending, state.currency, isToken); const pendingAmount = getPendingAmount(pending, state.currency, isToken);
if (isToken) { if (isToken) {
const token = findToken(tokens, account.address, state.currency, account.deviceState); const token = findToken(tokens, account.descriptor, state.currency, account.deviceState);
if (token) { if (token) {
state.amount = new BigNumber(token.balance).minus(pendingAmount).toString(10); state.amount = new BigNumber(token.balance).minus(pendingAmount).toString(10);
} }
@ -173,7 +172,7 @@ export const addressLabel = ($state: State): PayloadAction<State> => (dispatch:
if (!account || !network) return state; if (!account || !network) return state;
const { address } = state; const { address } = state;
const savedAccounts = getState().accounts.filter(a => a.address.toLowerCase() === address.toLowerCase()); const savedAccounts = getState().accounts.filter(a => a.descriptor.toLowerCase() === address.toLowerCase());
if (savedAccounts.length > 0) { if (savedAccounts.length > 0) {
// check if found account belongs to this network // check if found account belongs to this network
const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut); const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut);
@ -221,7 +220,7 @@ export const amountValidation = ($state: State): PayloadAction<State> => (dispat
const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, isToken); const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, isToken);
if (isToken) { if (isToken) {
const token = findToken(tokens, account.address, state.currency, account.deviceState); const token = findToken(tokens, account.descriptor, state.currency, account.deviceState);
if (!token) return state; if (!token) return state;
const decimalRegExp = dynamicRegexp(parseInt(token.decimals, 0)); const decimalRegExp = dynamicRegexp(parseInt(token.decimals, 0));
@ -304,7 +303,7 @@ export const nonceValidation = ($state: State): PayloadAction<State> => (dispatc
const { const {
account, account,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account) return state; if (!account || account.networkType !== 'ethereum') return state;
const { nonce } = state; const { nonce } = state;
if (nonce.length < 1) { if (nonce.length < 1) {

View File

@ -16,7 +16,7 @@ import type {
const DECIMALS: number = 6; const DECIMALS: number = 6;
export const subscribe = (network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const subscribe = (network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const accounts: Array<string> = getState().accounts.filter(a => a.network === network).map(a => a.address); const accounts: Array<string> = getState().accounts.filter(a => a.network === network).map(a => a.descriptor);
await TrezorConnect.blockchainSubscribe({ await TrezorConnect.blockchainSubscribe({
accounts, accounts,
coin: network, coin: network,
@ -68,7 +68,7 @@ export const onBlockMined = (network: string): PromiseAction<void> => async (dis
export const onNotification = (payload: $ElementType<BlockchainNotification, 'payload'>): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const onNotification = (payload: $ElementType<BlockchainNotification, 'payload'>): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { notification } = payload; const { notification } = payload;
const account = getState().accounts.find(a => a.address === notification.address); const account = getState().accounts.find(a => a.descriptor === notification.address);
if (!account) return; if (!account) return;
if (notification.status === 'pending') { if (notification.status === 'pending') {
@ -95,7 +95,7 @@ export const onNotification = (payload: $ElementType<BlockchainNotification, 'pa
const updatedAccount = await TrezorConnect.rippleGetAccountInfo({ const updatedAccount = await TrezorConnect.rippleGetAccountInfo({
account: { account: {
address: account.address, address: account.descriptor,
from: account.block, from: account.block,
history: false, history: false,
}, },
@ -104,10 +104,12 @@ export const onNotification = (payload: $ElementType<BlockchainNotification, 'pa
if (!updatedAccount.success) return; if (!updatedAccount.success) return;
dispatch(AccountsActions.update({ dispatch(AccountsActions.update({
networkType: 'ripple',
...account, ...account,
balance: toDecimalAmount(updatedAccount.payload.balance, DECIMALS), balance: toDecimalAmount(updatedAccount.payload.balance, DECIMALS),
availableBalance: toDecimalAmount(updatedAccount.payload.availableBalance, DECIMALS), availableBalance: toDecimalAmount(updatedAccount.payload.availableBalance, DECIMALS),
block: updatedAccount.payload.block, block: updatedAccount.payload.block,
sequence: updatedAccount.payload.sequence, sequence: updatedAccount.payload.sequence,
reserve: '0',
})); }));
}; };

View File

@ -60,19 +60,22 @@ export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discover
const empty = account.sequence <= 0 && account.balance === '0'; const empty = account.sequence <= 0 && account.balance === '0';
return { return {
imported: false,
index: discoveryProcess.accountIndex, index: discoveryProcess.accountIndex,
loaded: true,
network: network.shortcut, network: network.shortcut,
deviceID: device.features ? device.features.device_id : '0', deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0', deviceState: device.state || '0',
addressPath: account.path || [], accountPath: account.path || [],
address: account.address, descriptor: account.address,
balance: toDecimalAmount(account.balance, 6),
availableBalance: toDecimalAmount(account.availableBalance, 6), balance: toDecimalAmount(account.balance, network.decimals),
sequence: account.sequence, availableBalance: toDecimalAmount(account.availableBalance, network.decimals),
nonce: account.sequence,
block: account.block, block: account.block,
transactions: account.transactions, transactions: account.transactions,
empty, empty,
networkType: 'ripple',
sequence: account.sequence,
reserve: '0',
}; };
}; };

View File

@ -173,7 +173,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
if (!account || !network) return; if (!account || account.networkType !== 'ripple' || !network) return;
const blockchain = getState().blockchain.find(b => b.shortcut === account.network); const blockchain = getState().blockchain.find(b => b.shortcut === account.network);
if (!blockchain) return; if (!blockchain) return;
@ -188,7 +188,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
state: selected.state, state: selected.state,
}, },
useEmptyPassphrase: selected.useEmptyPassphrase, useEmptyPassphrase: selected.useEmptyPassphrase,
path: account.addressPath, path: account.accountPath,
transaction: { transaction: {
fee: blockchain.fee, // Fee must be in the range of 10 to 10,000 drops fee: blockchain.fee, // Fee must be in the range of 10 to 10,000 drops
flags: 0x80000000, flags: 0x80000000,

View File

@ -115,7 +115,7 @@ const addressValidation = ($state: State): PayloadAction<State> => (dispatch: Di
state.errors.address = 'Address is not set'; state.errors.address = 'Address is not set';
} else if (!AddressValidator.validate(address, 'XRP')) { } else if (!AddressValidator.validate(address, 'XRP')) {
state.errors.address = 'Address is not valid'; state.errors.address = 'Address is not valid';
} else if (address.toLowerCase() === account.address.toLowerCase()) { } else if (address.toLowerCase() === account.descriptor.toLowerCase()) {
state.errors.address = 'Cannot send to myself'; state.errors.address = 'Cannot send to myself';
} }
return state; return state;
@ -135,7 +135,7 @@ const addressLabel = ($state: State): PayloadAction<State> => (dispatch: Dispatc
if (!account || !network) return state; if (!account || !network) return state;
const { address } = state; const { address } = state;
const savedAccounts = getState().accounts.filter(a => a.address.toLowerCase() === address.toLowerCase()); const savedAccounts = getState().accounts.filter(a => a.descriptor.toLowerCase() === address.toLowerCase());
if (savedAccounts.length > 0) { if (savedAccounts.length > 0) {
// check if found account belongs to this network // check if found account belongs to this network
const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut); const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut);

View File

@ -45,7 +45,7 @@ const ConfirmAddress = (props: Props) => {
<P>Please compare your address on device with address shown bellow.</P> <P>Please compare your address on device with address shown bellow.</P>
</Header> </Header>
<Content> <Content>
<P>{ account.address }</P> <P>{ account.descriptor }</P>
<Label>{ network.symbol } account #{ (account.index + 1) }</Label> <Label>{ network.symbol } account #{ (account.index + 1) }</Label>
</Content> </Content>
</Wrapper> </Wrapper>

View File

@ -71,7 +71,7 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
const { account, onCancel, showAddress } = this.props; const { account, onCancel, showAddress } = this.props;
if (!account) return; if (!account) return;
onCancel(); onCancel();
showAddress(account.addressPath); showAddress(account.accountPath);
} }
showUnverifiedAddress() { showUnverifiedAddress() {

View File

@ -47,7 +47,7 @@ import type {
BlockchainLinkTransaction, BlockchainLinkTransaction,
} from 'trezor-connect'; } from 'trezor-connect';
import type { RouterAction, LocationState } from 'react-router-redux'; import type { RouterAction, LocationState } from 'connected-react-router';
export type AcquiredDevice = $Exact<{ export type AcquiredDevice = $Exact<{
+type: 'acquired', +type: 'acquired',

View File

@ -3,7 +3,7 @@ import type {
Location as RouterLocation, Location as RouterLocation,
} from 'react-router'; } from 'react-router';
declare module 'react-router-redux' { declare module 'connected-react-router' {
// custom state for location // custom state for location
declare export type LocationState = {[key: string]: string}; declare export type LocationState = {[key: string]: string};
@ -19,7 +19,9 @@ declare module 'react-router-redux' {
declare export type RouterAction = { declare export type RouterAction = {
type: typeof LOCATION_CHANGE, type: typeof LOCATION_CHANGE,
payload: Location; payload: {
location: Location,
},
} }
declare export type State = { declare export type State = {
@ -35,8 +37,9 @@ declare module 'react-router-redux' {
//declare export function routerReducer<S, A>(state?: S, action: A): S; //declare export function routerReducer<S, A>(state?: S, action: A): S;
declare export function routerReducer(state?: State, action: any): State; declare export function routerReducer(state?: State, action: any): State;
declare export function routerMiddleware(history: any): any; declare export function routerMiddleware(history: any): any;
declare export function connectRouter(history: any): any;
declare export class ConnectedRouter extends React$Component<{ declare export class ConnectedRouter extends React$Component<{
history: any history: any
}> {} }> {}
} }

View File

@ -6,27 +6,35 @@ import * as WALLET from 'actions/constants/wallet';
import * as ACCOUNT from 'actions/constants/account'; import * as ACCOUNT from 'actions/constants/account';
import type { Action, TrezorDevice } from 'flowtype'; import type { Action, TrezorDevice } from 'flowtype';
import type {
AccountSetBalanceAction,
AccountSetNonceAction,
} from 'actions/AccountsActions';
export type Account = { type AccountCommon = {
loaded: boolean; +imported: boolean,
+network: string; +index: number,
+deviceID: string; +network: string, // network id (shortcut)
+deviceState: string; +deviceID: string, // empty for imported accounts
+index: number; +deviceState: string, // empty for imported accounts
+addressPath: Array<number>; +accountPath: Array<number>, // empty for imported accounts
+address: string; +descriptor: string, // address or xpub
balance: string;
availableBalance: string; balance: string,
sequence: number; availableBalance: string, // balance - pending
nonce: number; block: number, // last known (synchronized) block
block: number; empty: boolean, // account without transactions
transactions: number;
empty: boolean; transactions: number, // deprecated
} };
export type Account = AccountCommon & {
networkType: 'ethereum',
nonce: number,
} | AccountCommon & {
networkType: 'ripple',
sequence: number,
reserve: string,
} | AccountCommon & {
networkType: 'bitcoin',
addressIndex: number,
};
export type State = Array<Account>; export type State = Array<Account>;
@ -44,7 +52,7 @@ export const findDeviceAccounts = (state: State, device: TrezorDevice, network:
const createAccount = (state: State, account: Account): State => { const createAccount = (state: State, account: Account): State => {
// TODO check with device_id // TODO check with device_id
// check if account was created before // check if account was created before
const exist: ?Account = state.find(a => a.address === account.address && a.network === account.network && a.deviceState === account.deviceState); const exist: ?Account = state.find(a => a.descriptor === account.descriptor && a.network === account.network && a.deviceState === account.deviceState);
if (exist) { if (exist) {
return state; return state;
} }
@ -55,7 +63,6 @@ const createAccount = (state: State, account: Account): State => {
const removeAccounts = (state: State, device: TrezorDevice): State => state.filter(account => account.deviceState !== device.state); const removeAccounts = (state: State, device: TrezorDevice): State => state.filter(account => account.deviceState !== device.state);
const clear = (state: State, devices: Array<TrezorDevice>): State => { const clear = (state: State, devices: Array<TrezorDevice>): State => {
let newState: State = [...state]; let newState: State = [...state];
devices.forEach((d) => { devices.forEach((d) => {
@ -65,29 +72,12 @@ const clear = (state: State, devices: Array<TrezorDevice>): State => {
}; };
const updateAccount = (state: State, account: Account): State => { const updateAccount = (state: State, account: Account): State => {
const index: number = state.findIndex(a => a.address === account.address && a.network === account.network && a.deviceState === account.deviceState); const index: number = state.findIndex(a => a.descriptor === account.descriptor && a.network === account.network && a.deviceState === account.deviceState);
const newState: State = [...state]; const newState: State = [...state];
newState[index] = account; newState[index] = account;
return newState; return newState;
}; };
const setBalance = (state: State, action: AccountSetBalanceAction): State => {
// const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState);
const index: number = state.findIndex(account => account.address === action.address && account.network === action.network);
const newState: State = [...state];
newState[index].loaded = true;
newState[index].balance = action.balance;
return newState;
};
const setNonce = (state: State, action: AccountSetNonceAction): State => {
const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState);
const newState: State = [...state];
newState[index].loaded = true;
newState[index].nonce = action.nonce;
return newState;
};
export default (state: State = initialState, action: Action): State => { export default (state: State = initialState, action: Action): State => {
switch (action.type) { switch (action.type) {
case ACCOUNT.CREATE: case ACCOUNT.CREATE:
@ -108,11 +98,6 @@ export default (state: State = initialState, action: Action): State => {
case ACCOUNT.UPDATE: case ACCOUNT.UPDATE:
return updateAccount(state, action.payload); return updateAccount(state, action.payload);
case ACCOUNT.SET_BALANCE:
return setBalance(state, action);
case ACCOUNT.SET_NONCE:
return setNonce(state, action);
case ACCOUNT.FROM_STORAGE: case ACCOUNT.FROM_STORAGE:
return action.payload; return action.payload;

View File

@ -1,7 +1,7 @@
/* @flow */ /* @flow */
import * as React from 'react'; import * as React from 'react';
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'connected-react-router';
import * as NOTIFICATION from 'actions/constants/notification'; import * as NOTIFICATION from 'actions/constants/notification';
import { DEVICE } from 'trezor-connect'; import { DEVICE } from 'trezor-connect';

View File

@ -6,7 +6,6 @@ import * as WALLET from 'actions/constants/wallet';
import * as TOKEN from 'actions/constants/token'; import * as TOKEN from 'actions/constants/token';
import type { Action, TrezorDevice } from 'flowtype'; import type { Action, TrezorDevice } from 'flowtype';
import type { Account } from './AccountsReducer';
export type Token = { export type Token = {
loaded: boolean; loaded: boolean;
@ -24,21 +23,6 @@ export type State = Array<Token>;
const initialState: State = []; const initialState: State = [];
// Helper for actions
export const findToken = (state: Array<Token>, address: string, symbol: string, deviceState: string): ?Token => state.find(t => t.ethAddress === address && t.symbol === symbol && t.deviceState === deviceState);
export const findAccountTokens = (state: Array<Token>, account: Account): Array<Token> => state.filter(t => t.ethAddress === account.address && t.network === account.network && t.deviceState === account.deviceState);
// const setBalance = (state: State, payload: any): State => {
// const newState: Array<Token> = [ ...state ];
// let index: number = state.findIndex(t => t.address === payload.address && t.ethAddress === payload.ethAddress);
// if (index >= 0) {
// newState[index].loaded = true;
// newState[index].balance = payload.balance;
// }
// return newState;
// }
const create = (state: State, token: Token): State => { const create = (state: State, token: Token): State => {
const newState: State = [...state]; const newState: State = [...state];
newState.push(token); newState.push(token);

View File

@ -1,7 +1,7 @@
/* @flow */ /* @flow */
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'connected-react-router';
import { DEVICE, TRANSPORT } from 'trezor-connect'; import { DEVICE, TRANSPORT } from 'trezor-connect';
import * as MODAL from 'actions/constants/modal'; import * as MODAL from 'actions/constants/modal';
import * as WALLET from 'actions/constants/wallet'; import * as WALLET from 'actions/constants/wallet';
@ -16,6 +16,7 @@ type State = {
showBetaDisclaimer: boolean; showBetaDisclaimer: boolean;
initialParams: ?RouterLocationState; initialParams: ?RouterLocationState;
initialPathname: ?string; initialPathname: ?string;
firstLocationChange: boolean;
disconnectRequest: ?TrezorDevice; disconnectRequest: ?TrezorDevice;
selectedDevice: ?TrezorDevice; selectedDevice: ?TrezorDevice;
} }
@ -24,6 +25,7 @@ const initialState: State = {
ready: false, ready: false,
online: navigator.onLine, online: navigator.onLine,
dropdownOpened: false, dropdownOpened: false,
firstLocationChange: true,
showBetaDisclaimer: false, showBetaDisclaimer: false,
initialParams: null, initialParams: null,
initialPathname: null, initialPathname: null,
@ -39,6 +41,11 @@ export default function wallet(state: State = initialState, action: Action): Sta
initialParams: action.state, initialParams: action.state,
initialPathname: action.pathname, initialPathname: action.pathname,
}; };
case WALLET.SET_FIRST_LOCATION_CHANGE:
return {
...state,
firstLocationChange: false,
};
case TRANSPORT.START: case TRANSPORT.START:
return { return {

View File

@ -1,6 +1,7 @@
/* @flow */ /* @flow */
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux'; import { connectRouter } from 'connected-react-router';
import type { State } from 'connected-react-router';
import log from 'reducers/LogReducer'; import log from 'reducers/LogReducer';
import localStorage from 'reducers/LocalStorageReducer'; import localStorage from 'reducers/LocalStorageReducer';
@ -24,7 +25,6 @@ import blockchain from 'reducers/BlockchainReducer';
import signVerify from 'reducers/SignVerifyReducer'; import signVerify from 'reducers/SignVerifyReducer';
const reducers = { const reducers = {
router: routerReducer,
log, log,
localStorage, localStorage,
connect, connect,
@ -45,10 +45,18 @@ const reducers = {
devices, devices,
blockchain, blockchain,
signVerify, signVerify,
router: () => ({
location: {
pathname: '', hash: '', search: '', state: {},
},
}: State),
}; };
export type Reducers = typeof reducers; export type Reducers = typeof reducers;
type $ExtractFunctionReturn = <V>(v: (...args: any) => V) => V; type $ExtractFunctionReturn = <V>(v: (...args: any) => V) => V;
export type ReducersState = $ObjMap<Reducers, $ExtractFunctionReturn>; export type ReducersState = $ObjMap<Reducers, $ExtractFunctionReturn>;
export default combineReducers(reducers); export default (history: any) => combineReducers({
...reducers,
router: connectRouter(history),
});

View File

@ -86,7 +86,7 @@ export const getDiscoveryProcess = (state: State): ?Discovery => {
export const getAccountPendingTx = (pending: Array<Transaction>, account: ?Account): Array<Transaction> => { export const getAccountPendingTx = (pending: Array<Transaction>, account: ?Account): Array<Transaction> => {
const a = account; const a = account;
if (!a) return []; if (!a) return [];
return pending.filter(p => p.network === a.network && p.address === a.address); return pending.filter(p => p.network === a.network && p.address === a.descriptor);
}; };
export const getPendingSequence = (pending: Array<Transaction>): number => pending.reduce((value: number, tx: Transaction) => { export const getPendingSequence = (pending: Array<Transaction>): number => pending.reduce((value: number, tx: Transaction) => {
@ -101,10 +101,12 @@ export const getPendingAmount = (pending: Array<Transaction>, currency: string,
return value; return value;
}, new BigNumber('0')); }, new BigNumber('0'));
export const getAccountTokens = (state: State, account: ?Account): Array<Token> => { export const findToken = (state: Array<Token>, address: string, symbol: string, deviceState: string): ?Token => state.find(t => t.ethAddress === address && t.symbol === symbol && t.deviceState === deviceState);
export const getAccountTokens = (tokens: Array<Token>, account: ?Account): Array<Token> => {
const a = account; const a = account;
if (!a) return []; if (!a) return [];
return state.tokens.filter(t => t.ethAddress === a.address && t.network === a.network && t.deviceState === a.deviceState); return tokens.filter(t => t.ethAddress === a.descriptor && t.network === a.network && t.deviceState === a.deviceState);
}; };
export const getWeb3 = (state: State): ?Web3Instance => { export const getWeb3 = (state: State): ?Web3Instance => {

View File

@ -37,8 +37,7 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar
break; break;
case ACCOUNT.CREATE: case ACCOUNT.CREATE:
case ACCOUNT.SET_BALANCE: case ACCOUNT.UPDATE:
case ACCOUNT.SET_NONCE:
api.dispatch(LocalStorageActions.save()); api.dispatch(LocalStorageActions.save());
break; break;

View File

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
import { LOCATION_CHANGE, replace } from 'react-router-redux'; import { LOCATION_CHANGE, replace } from 'connected-react-router';
import * as RouterActions from 'actions/RouterActions'; import * as RouterActions from 'actions/RouterActions';
import type { import type {
@ -25,11 +25,11 @@ const RouterService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
const validUrl = api.dispatch(RouterActions.getValidUrl(action)); const validUrl = api.dispatch(RouterActions.getValidUrl(action));
// override action state (to be stored in RouterReducer) // override action state (to be stored in RouterReducer)
const override = action; const override = action;
override.payload.state = api.dispatch(RouterActions.pathToParams(validUrl)); override.payload.location.state = api.dispatch(RouterActions.pathToParams(validUrl));
const redirect = action.payload.pathname !== validUrl; const redirect = action.payload.location.pathname !== validUrl;
if (redirect) { if (redirect) {
// override action pathname // override action pathname
override.payload.pathname = validUrl; override.payload.location.pathname = validUrl;
} }
// pass action // pass action

View File

@ -1,6 +1,6 @@
/* @flow */ /* @flow */
import { DEVICE } from 'trezor-connect'; import { DEVICE } from 'trezor-connect';
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'connected-react-router';
import * as WALLET from 'actions/constants/wallet'; import * as WALLET from 'actions/constants/wallet';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
@ -25,17 +25,16 @@ import type {
*/ */
const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => async (action: Action): Promise<Action> => { const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => async (action: Action): Promise<Action> => {
const prevState = api.getState(); const prevState = api.getState();
// Application live cycle starts HERE! // Application live cycle starts HERE!
// when first LOCATION_CHANGE is called router does not have "location" set yet // when first LOCATION_CHANGE is called router does not have "location" set yet
if (action.type === LOCATION_CHANGE && !prevState.router.location) { if (action.type === LOCATION_CHANGE && prevState.wallet.firstLocationChange) {
// initialize wallet // initialize wallet
api.dispatch(WalletActions.init()); api.dispatch(WalletActions.init());
// set initial url // set initial url
// TODO: validate if initial url is potentially correct // TODO: validate if initial url is potentially correct
api.dispatch({ api.dispatch({
type: WALLET.SET_INITIAL_URL, type: WALLET.SET_INITIAL_URL,
pathname: action.payload.pathname, pathname: action.payload.location.pathname,
state: {}, state: {},
}); });
// pass action and break process // pass action and break process

View File

@ -1,11 +1,11 @@
/* @flow */ /* @flow */
import { createStore, applyMiddleware, compose } from 'redux'; import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'react-router-redux'; import { routerMiddleware } from 'connected-react-router';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import createHistory from 'history/createHashHistory'; import createHistory from 'history/createHashHistory';
import { createLogger } from 'redux-logger'; import { createLogger } from 'redux-logger';
import reducers from 'reducers'; import createRootReducer from 'reducers';
import services from 'services'; import services from 'services';
import Raven from 'raven-js'; import Raven from 'raven-js';
@ -65,7 +65,7 @@ if (buildUtils.isDev()) {
} }
export default createStore( export default createStore(
reducers, createRootReducer(history),
initialState, initialState,
composedEnhancers, composedEnhancers,
); );

View File

@ -161,10 +161,8 @@ const AccountMenu = (props: Props) => {
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network); const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
if (discovery && discovery.completed) { if (discovery && discovery.completed) {
// TODO: add only if last one is not empty
//if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
const lastAccount = deviceAccounts[deviceAccounts.length - 1]; const lastAccount = deviceAccounts[deviceAccounts.length - 1];
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) { if (lastAccount && !lastAccount.empty) {
discoveryStatus = ( discoveryStatus = (
<Row onClick={props.addAccount}> <Row onClick={props.addAccount}>
<RowAddAccountWrapper> <RowAddAccountWrapper>

View File

@ -111,9 +111,9 @@ const AccountReceive = (props: Props) => {
const isAddressVerifying = props.modal.context === CONTEXT_DEVICE && props.modal.windowType === 'ButtonRequest_Address'; const isAddressVerifying = props.modal.context === CONTEXT_DEVICE && props.modal.windowType === 'ButtonRequest_Address';
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified; const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
let address = `${account.address.substring(0, 20)}...`; let address = `${account.descriptor.substring(0, 20)}...`;
if (addressVerified || addressUnverified || isAddressVerifying) { if (addressVerified || addressUnverified || isAddressVerifying) {
({ address } = account); address = account.descriptor;
} }
return ( return (
@ -149,7 +149,7 @@ const AccountReceive = (props: Props) => {
/> />
)} )}
> >
<EyeButton onClick={() => props.showAddress(account.addressPath)}> <EyeButton onClick={() => props.showAddress(account.accountPath)}>
<Icon <Icon
icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE} icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE}
color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY} color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY}
@ -159,7 +159,7 @@ const AccountReceive = (props: Props) => {
)} )}
/> />
{!(addressVerified || addressUnverified) && ( {!(addressVerified || addressUnverified) && (
<ShowAddressButton onClick={() => props.showAddress(account.addressPath)} isDisabled={device.connected && !discovery.completed}> <ShowAddressButton onClick={() => props.showAddress(account.accountPath)} isDisabled={device.connected && !discovery.completed}>
<ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address <ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address
</ShowAddressButton> </ShowAddressButton>
)} )}
@ -172,7 +172,7 @@ const AccountReceive = (props: Props) => {
fgColor="#000000" fgColor="#000000"
level="Q" level="Q"
style={{ width: 150 }} style={{ width: 150 }}
value={account.address} value={account.descriptor}
/> />
</QrWrapper> </QrWrapper>
)} )}

View File

@ -111,9 +111,9 @@ const AccountReceive = (props: Props) => {
const isAddressVerifying = props.modal.context === CONTEXT_DEVICE && props.modal.windowType === 'ButtonRequest_Address'; const isAddressVerifying = props.modal.context === CONTEXT_DEVICE && props.modal.windowType === 'ButtonRequest_Address';
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified; const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
let address = `${account.address.substring(0, 20)}...`; let address = `${account.descriptor.substring(0, 20)}...`;
if (addressVerified || addressUnverified || isAddressVerifying) { if (addressVerified || addressUnverified || isAddressVerifying) {
({ address } = account); address = account.descriptor;
} }
return ( return (
@ -149,7 +149,7 @@ const AccountReceive = (props: Props) => {
/> />
)} )}
> >
<EyeButton onClick={() => props.showAddress(account.addressPath)}> <EyeButton onClick={() => props.showAddress(account.accountPath)}>
<Icon <Icon
icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE} icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE}
color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY} color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY}
@ -159,7 +159,7 @@ const AccountReceive = (props: Props) => {
)} )}
/> />
{!(addressVerified || addressUnverified) && ( {!(addressVerified || addressUnverified) && (
<ShowAddressButton onClick={() => props.showAddress(account.addressPath)} isDisabled={device.connected && !discovery.completed}> <ShowAddressButton onClick={() => props.showAddress(account.accountPath)} isDisabled={device.connected && !discovery.completed}>
<ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address <ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address
</ShowAddressButton> </ShowAddressButton>
)} )}
@ -172,7 +172,7 @@ const AccountReceive = (props: Props) => {
fgColor="#000000" fgColor="#000000"
level="Q" level="Q"
style={{ width: 150 }} style={{ width: 150 }}
value={account.address} value={account.descriptor}
/> />
</QrWrapper> </QrWrapper>
)} )}

View File

@ -104,7 +104,7 @@ class SignVerify extends Component <Props> {
<Input <Input
topLabel="Address" topLabel="Address"
name="signAddress" name="signAddress"
value={account.address} value={account.descriptor}
type="text" type="text"
autoSelect autoSelect
readOnly readOnly
@ -140,7 +140,7 @@ class SignVerify extends Component <Props> {
>Clear >Clear
</StyledButton> </StyledButton>
<StyledButton <StyledButton
onClick={() => signVerifyActions.sign(account.addressPath, signMessage)} onClick={() => signVerifyActions.sign(account.accountPath, signMessage)}
>Sign >Sign
</StyledButton> </StyledButton>
</RowButtons> </RowButtons>

View File

@ -84,7 +84,7 @@ const AccountSummary = (props: Props) => {
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />; return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
} }
const explorerLink: string = `${network.explorer.address}${account.address}`; const explorerLink: string = `${network.explorer.address}${account.descriptor}`;
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol); const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10); const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10);

View File

@ -74,7 +74,7 @@ const AccountSummary = (props: Props) => {
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />; return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
} }
const explorerLink: string = `${network.explorer.address}${account.address}`; const explorerLink: string = `${network.explorer.address}${account.descriptor}`;
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol); const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10); const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10);

View File

@ -2,7 +2,7 @@ import React from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux'; import { ConnectedRouter } from 'connected-react-router';
// general // general
import ErrorBoundary from 'support/ErrorBoundary'; import ErrorBoundary from 'support/ErrorBoundary';

115
yarn.lock
View File

@ -127,6 +127,13 @@
version "7.0.0-rc.1" version "7.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-rc.1.tgz#d009a9bba8175d7b971e30cd03535b278c44082d" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-rc.1.tgz#d009a9bba8175d7b971e30cd03535b278c44082d"
"@babel/runtime@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.2.0.tgz#b03e42eeddf5898e00646e4c840fa07ba8dcad7f"
integrity sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==
dependencies:
regenerator-runtime "^0.12.0"
"@babel/template@7.0.0-beta.44": "@babel/template@7.0.0-beta.44":
version "7.0.0-beta.44" version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
@ -2611,6 +2618,14 @@ connect-history-api-fallback@^1.3.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a"
connected-react-router@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.0.0.tgz#cb7ccbbc5ed353832ecd91d68289c916e8aba734"
integrity sha512-TarPqf2wY3cz993Mw3eBg2U12M5OmaGwKzJsinvRQh61nKb8WMUvimyhu6u2HeWS8625PHFXjNOU0OIAMWj/bQ==
dependencies:
immutable "^3.8.1"
seamless-immutable "^7.1.3"
console-browserify@^1.1.0: console-browserify@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@ -4822,6 +4837,13 @@ hoist-non-react-statics@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40"
hoist-non-react-statics@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.2.1.tgz#c09c0555c84b38a7ede6912b61efddafd6e75e1e"
integrity sha512-TFsu3TV3YLY+zFTZDrN8L2DTFanObwmBLpWvJs1qfUuEQ5bTAdFcwfx2T/bsCXfM9QHSLvjfP+nihEl0yvozxw==
dependencies:
react-is "^16.3.2"
home-or-tmp@^2.0.0: home-or-tmp@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@ -5009,6 +5031,11 @@ ignore@^3.3.5:
version "3.3.7" version "3.3.7"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
immutable@^3.8.1:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
import-local@^1.0.0: import-local@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
@ -5111,15 +5138,16 @@ into-stream@^3.1.0:
from2 "^2.1.1" from2 "^2.1.1"
p-is-promise "^1.1.0" p-is-promise "^1.1.0"
invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2: invariant@^2.2.0, invariant@^2.2.4:
version "2.2.2" version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
invariant@^2.2.0: invariant@^2.2.1, invariant@^2.2.2:
version "2.2.4" version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A=
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
@ -5872,6 +5900,11 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
version "3.12.0" version "3.12.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
@ -6227,10 +6260,6 @@ locate-path@^2.0.0:
p-locate "^2.0.0" p-locate "^2.0.0"
path-exists "^3.0.0" path-exists "^3.0.0"
lodash-es@^4.17.5:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.5.tgz#9fc6e737b1c4d151d8f9cae2247305d552ce748f"
lodash._getnative@^3.0.0: lodash._getnative@^3.0.0:
version "3.9.1" version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
@ -6331,6 +6360,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
dependencies: dependencies:
js-tokens "^3.0.0" js-tokens "^3.0.0"
loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
loud-rejection@^1.0.0, loud-rejection@^1.6.0: loud-rejection@^1.0.0, loud-rejection@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@ -7986,7 +8022,7 @@ react-input-autosize@^2.2.1:
dependencies: dependencies:
prop-types "^15.5.8" prop-types "^15.5.8"
react-is@^16.6.0: react-is@^16.3.2, react-is@^16.6.0, react-is@^16.6.3:
version "16.6.3" version "16.6.3"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
@ -8010,16 +8046,17 @@ react-qr-svg@^2.1.0:
prop-types "^15.5.8" prop-types "^15.5.8"
qr.js "0.0.0" qr.js "0.0.0"
react-redux@^5.0.7: react-redux@^6.0.0:
version "5.0.7" version "6.0.0"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.0.tgz#09e86eeed5febb98e9442458ad2970c8f1a173ef"
integrity sha512-EmbC3uLl60pw2VqSSkj6HpZ6jTk12RMrwXMBdYtM6niq0MdEaRq9KYCwpJflkOZj349BLGQm1MI/JO1W96kLWQ==
dependencies: dependencies:
hoist-non-react-statics "^2.5.0" "@babel/runtime" "^7.2.0"
invariant "^2.0.0" hoist-non-react-statics "^3.2.1"
lodash "^4.17.5" invariant "^2.2.4"
lodash-es "^4.17.5" loose-envify "^1.4.0"
loose-envify "^1.1.0" prop-types "^15.6.2"
prop-types "^15.6.0" react-is "^16.6.3"
react-router-dom@^4.2.2: react-router-dom@^4.2.2:
version "4.2.2" version "4.2.2"
@ -8032,14 +8069,6 @@ react-router-dom@^4.2.2:
react-router "^4.2.0" react-router "^4.2.0"
warning "^3.0.0" warning "^3.0.0"
react-router-redux@next:
version "5.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-5.0.0-alpha.8.tgz#5242c705730b2ac862aff7a8e90f870d0cf45e12"
dependencies:
history "^4.7.2"
prop-types "^15.6.0"
react-router "^4.2.0"
react-router@^4.2.0: react-router@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986" resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986"
@ -8052,6 +8081,19 @@ react-router@^4.2.0:
prop-types "^15.5.4" prop-types "^15.5.4"
warning "^3.0.0" warning "^3.0.0"
react-router@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.3.1.tgz#aada4aef14c809cb2e686b05cee4742234506c4e"
integrity sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==
dependencies:
history "^4.7.2"
hoist-non-react-statics "^2.5.0"
invariant "^2.2.4"
loose-envify "^1.3.1"
path-to-regexp "^1.7.0"
prop-types "^15.6.1"
warning "^4.0.1"
react-scale-text@^1.2.2: react-scale-text@^1.2.2:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/react-scale-text/-/react-scale-text-1.2.2.tgz#4a56e1d2fd4e4582d2ad472c003ee12f51cbf2ae" resolved "https://registry.yarnpkg.com/react-scale-text/-/react-scale-text-1.2.2.tgz#4a56e1d2fd4e4582d2ad472c003ee12f51cbf2ae"
@ -8320,6 +8362,11 @@ regenerator-runtime@^0.11.0:
version "0.11.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-transform@^0.10.0: regenerator-transform@^0.10.0:
version "0.10.1" version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@ -8781,6 +8828,11 @@ scryptsy@^1.2.1:
dependencies: dependencies:
pbkdf2 "^3.0.3" pbkdf2 "^3.0.3"
seamless-immutable@^7.1.3:
version "7.1.4"
resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8"
integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==
secp256k1@^3.0.1: secp256k1@^3.0.1:
version "3.4.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.4.0.tgz#1c905b256fa4ae5b9cc170e672dd59b4c5de46a4" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.4.0.tgz#1c905b256fa4ae5b9cc170e672dd59b4c5de46a4"
@ -10304,6 +10356,13 @@ warning@^3.0.0:
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
warning@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607"
integrity sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==
dependencies:
loose-envify "^1.0.0"
watch@~0.18.0: watch@~0.18.0:
version "0.18.0" version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"