mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-30 20:28:09 +00:00
split discovery actions to coin specific files
This commit is contained in:
parent
198dd4e9c7
commit
0918403024
@ -1,7 +1,6 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
import EthereumjsUtil from 'ethereumjs-util';
|
|
||||||
import * as DISCOVERY from 'actions/constants/discovery';
|
import * as DISCOVERY from 'actions/constants/discovery';
|
||||||
import * as ACCOUNT from 'actions/constants/account';
|
import * as ACCOUNT from 'actions/constants/account';
|
||||||
import * as NOTIFICATION from 'actions/constants/notification';
|
import * as NOTIFICATION from 'actions/constants/notification';
|
||||||
@ -14,29 +13,25 @@ import type {
|
|||||||
GetState,
|
GetState,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
|
Account,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
import type { Discovery, State } from 'reducers/DiscoveryReducer';
|
import type { Discovery, State } from 'reducers/DiscoveryReducer';
|
||||||
import * as BlockchainActions from './BlockchainActions';
|
import * as BlockchainActions from './BlockchainActions';
|
||||||
|
import * as EthereumDiscoveryActions from './ethereum/EthereumDiscoveryActions';
|
||||||
|
import * as RippleDiscoveryActions from './ripple/RippleDiscoveryActions';
|
||||||
|
|
||||||
export type DiscoveryStartAction = {
|
export type DiscoveryStartAction = EthereumDiscoveryActions.DiscoveryStartAction | RippleDiscoveryActions.DiscoveryStartAction;
|
||||||
type: typeof DISCOVERY.START,
|
|
||||||
device: TrezorDevice,
|
|
||||||
network: string,
|
|
||||||
publicKey: string,
|
|
||||||
chainCode: string,
|
|
||||||
basePath: Array<number>,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DiscoveryWaitingAction = {
|
export type DiscoveryWaitingAction = {
|
||||||
type: typeof DISCOVERY.WAITING_FOR_DEVICE | typeof DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
type: typeof DISCOVERY.WAITING_FOR_DEVICE | typeof DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
||||||
device: TrezorDevice,
|
device: TrezorDevice,
|
||||||
network: string
|
network: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DiscoveryCompleteAction = {
|
export type DiscoveryCompleteAction = {
|
||||||
type: typeof DISCOVERY.COMPLETE,
|
type: typeof DISCOVERY.COMPLETE,
|
||||||
device: TrezorDevice,
|
device: TrezorDevice,
|
||||||
network: string
|
network: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DiscoveryAction = {
|
export type DiscoveryAction = {
|
||||||
@ -122,44 +117,40 @@ const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean)
|
|||||||
// first iteration
|
// first iteration
|
||||||
// generate public key for this account
|
// generate public key for this account
|
||||||
// start discovery process
|
// start discovery process
|
||||||
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
const begin = (device: TrezorDevice, networkName: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const { config } = getState().localStorage;
|
const { config } = getState().localStorage;
|
||||||
const networkData = config.networks.find(c => c.shortcut === network);
|
const network = config.networks.find(c => c.shortcut === networkName);
|
||||||
if (!networkData) return;
|
if (!network) return;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
type: DISCOVERY.WAITING_FOR_DEVICE,
|
||||||
device,
|
device,
|
||||||
network,
|
network: networkName,
|
||||||
});
|
});
|
||||||
|
|
||||||
// get xpub from TREZOR
|
let startAction: DiscoveryStartAction;
|
||||||
const response = await TrezorConnect.getPublicKey({
|
|
||||||
device: {
|
|
||||||
path: device.path,
|
|
||||||
instance: device.instance,
|
|
||||||
state: device.state,
|
|
||||||
},
|
|
||||||
path: networkData.bip44,
|
|
||||||
keepSession: true, // acquire and hold session
|
|
||||||
//useEmptyPassphrase: !device.instance,
|
|
||||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle TREZOR response error
|
try {
|
||||||
if (!response.success) {
|
if (network.type === 'ethereum') {
|
||||||
|
startAction = await dispatch(EthereumDiscoveryActions.begin(device, network));
|
||||||
|
} else if (network.type === 'ripple') {
|
||||||
|
startAction = await dispatch(RippleDiscoveryActions.begin(device, network));
|
||||||
|
} else {
|
||||||
|
throw new Error(`DiscoveryActions.begin: Unknown network type: ${network.type}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Discovery error',
|
title: 'Discovery error',
|
||||||
message: response.payload.error,
|
message: error.message,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
label: 'Try again',
|
label: 'Try again',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch(start(device, network));
|
dispatch(start(device, networkName));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -174,62 +165,27 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => async (dis
|
|||||||
const discoveryProcess = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
|
const discoveryProcess = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
||||||
|
|
||||||
const basePath: Array<number> = response.payload.path;
|
|
||||||
|
|
||||||
// send data to reducer
|
// send data to reducer
|
||||||
dispatch({
|
dispatch(startAction);
|
||||||
type: DISCOVERY.START,
|
|
||||||
network,
|
|
||||||
device,
|
|
||||||
publicKey: response.payload.publicKey,
|
|
||||||
chainCode: response.payload.chainCode,
|
|
||||||
basePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
// next iteration
|
// next iteration
|
||||||
dispatch(start(device, network));
|
dispatch(start(device, networkName));
|
||||||
};
|
};
|
||||||
|
|
||||||
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const { completed } = discoveryProcess;
|
const { config } = getState().localStorage;
|
||||||
|
const network = config.networks.find(c => c.shortcut === discoveryProcess.network);
|
||||||
|
if (!network) return;
|
||||||
|
|
||||||
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
|
const { completed, accountIndex } = discoveryProcess;
|
||||||
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
|
let account: Account;
|
||||||
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
|
|
||||||
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
|
|
||||||
const { network } = discoveryProcess;
|
|
||||||
|
|
||||||
// TODO: check if address was created before
|
|
||||||
try {
|
try {
|
||||||
const account = await dispatch(BlockchainActions.discoverAccount(device, ethAddress, network));
|
if (network.type === 'ethereum') {
|
||||||
// check for interruption
|
account = await dispatch(EthereumDiscoveryActions.discoverAccount(device, discoveryProcess));
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
} else if (network.type === 'ripple') {
|
||||||
|
account = await dispatch(RippleDiscoveryActions.discoverAccount(device, discoveryProcess));
|
||||||
// const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0';
|
} else {
|
||||||
const accountIsEmpty = account.nonce <= 0 && account.balance === '0';
|
throw new Error(`DiscoveryActions.discoverAccount: Unknown network type: ${network.type}`);
|
||||||
if (!accountIsEmpty || (accountIsEmpty && completed) || (accountIsEmpty && discoveryProcess.accountIndex === 0)) {
|
|
||||||
dispatch({
|
|
||||||
type: ACCOUNT.CREATE,
|
|
||||||
payload: {
|
|
||||||
index: discoveryProcess.accountIndex,
|
|
||||||
loaded: true,
|
|
||||||
network,
|
|
||||||
deviceID: device.features ? device.features.device_id : '0',
|
|
||||||
deviceState: device.state || '0',
|
|
||||||
addressPath: path,
|
|
||||||
address: ethAddress,
|
|
||||||
balance: account.balance,
|
|
||||||
nonce: account.nonce,
|
|
||||||
block: account.block,
|
|
||||||
transactions: account.transactions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountIsEmpty) {
|
|
||||||
dispatch(finish(device, discoveryProcess));
|
|
||||||
} else if (!completed) {
|
|
||||||
dispatch(discoverAccount(device, discoveryProcess));
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -254,6 +210,23 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
||||||
|
|
||||||
|
const accountIsEmpty = account.empty;
|
||||||
|
if (!accountIsEmpty || (accountIsEmpty && completed) || (accountIsEmpty && accountIndex === 0)) {
|
||||||
|
dispatch({
|
||||||
|
type: ACCOUNT.CREATE,
|
||||||
|
payload: account,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountIsEmpty) {
|
||||||
|
dispatch(finish(device, discoveryProcess));
|
||||||
|
} else if (!completed) {
|
||||||
|
dispatch(discoverAccount(device, discoveryProcess));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -269,7 +242,7 @@ const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction
|
|||||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||||
});
|
});
|
||||||
|
|
||||||
await dispatch(BlockchainActions.subscribe(discoveryProcess.network));
|
// await dispatch(BlockchainActions.subscribe(discoveryProcess.network));
|
||||||
|
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
||||||
|
|
||||||
|
90
src/actions/ethereum/EthereumDiscoveryActions.js
Normal file
90
src/actions/ethereum/EthereumDiscoveryActions.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import TrezorConnect from 'trezor-connect';
|
||||||
|
import EthereumjsUtil from 'ethereumjs-util';
|
||||||
|
import * as DISCOVERY from 'actions/constants/discovery';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
PromiseAction,
|
||||||
|
Dispatch,
|
||||||
|
TrezorDevice,
|
||||||
|
Network,
|
||||||
|
Account,
|
||||||
|
} from 'flowtype';
|
||||||
|
import type { Discovery } from 'reducers/DiscoveryReducer';
|
||||||
|
import * as BlockchainActions from '../BlockchainActions';
|
||||||
|
|
||||||
|
export type DiscoveryStartAction = {
|
||||||
|
type: typeof DISCOVERY.START,
|
||||||
|
networkType: 'ethereum',
|
||||||
|
network: Network,
|
||||||
|
device: TrezorDevice,
|
||||||
|
publicKey: string,
|
||||||
|
chainCode: string,
|
||||||
|
basePath: Array<number>,
|
||||||
|
};
|
||||||
|
|
||||||
|
// first iteration
|
||||||
|
// generate public key for this account
|
||||||
|
// start discovery process
|
||||||
|
export const begin = (device: TrezorDevice, network: Network): PromiseAction<DiscoveryStartAction> => async (): Promise<DiscoveryStartAction> => {
|
||||||
|
// get xpub from TREZOR
|
||||||
|
const response = await TrezorConnect.getPublicKey({
|
||||||
|
device: {
|
||||||
|
path: device.path,
|
||||||
|
instance: device.instance,
|
||||||
|
state: device.state,
|
||||||
|
},
|
||||||
|
path: network.bip44,
|
||||||
|
keepSession: true, // acquire and hold session
|
||||||
|
//useEmptyPassphrase: !device.instance,
|
||||||
|
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||||
|
network: network.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle TREZOR response error
|
||||||
|
if (!response.success) {
|
||||||
|
throw new Error(response.payload.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePath: Array<number> = response.payload.path;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: DISCOVERY.START,
|
||||||
|
networkType: 'ethereum',
|
||||||
|
network,
|
||||||
|
device,
|
||||||
|
publicKey: response.payload.publicKey,
|
||||||
|
chainCode: response.payload.chainCode,
|
||||||
|
basePath,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): PromiseAction<Account> => async (dispatch: Dispatch): Promise<Account> => {
|
||||||
|
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
|
||||||
|
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
|
||||||
|
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
|
||||||
|
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
|
||||||
|
const { network } = discoveryProcess;
|
||||||
|
|
||||||
|
// TODO: check if address was created before
|
||||||
|
const account = await dispatch(BlockchainActions.discoverAccount(device, ethAddress, network));
|
||||||
|
|
||||||
|
// const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0';
|
||||||
|
const empty = account.nonce <= 0 && account.balance === '0';
|
||||||
|
|
||||||
|
return {
|
||||||
|
index: discoveryProcess.accountIndex,
|
||||||
|
loaded: true,
|
||||||
|
network,
|
||||||
|
deviceID: device.features ? device.features.device_id : '0',
|
||||||
|
deviceState: device.state || '0',
|
||||||
|
addressPath: path,
|
||||||
|
address: ethAddress,
|
||||||
|
balance: account.balance,
|
||||||
|
nonce: account.nonce,
|
||||||
|
block: account.block,
|
||||||
|
transactions: account.transactions,
|
||||||
|
empty,
|
||||||
|
};
|
||||||
|
};
|
@ -2,166 +2,37 @@
|
|||||||
|
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
import * as DISCOVERY from 'actions/constants/discovery';
|
import * as DISCOVERY from 'actions/constants/discovery';
|
||||||
import * as ACCOUNT from 'actions/constants/account';
|
|
||||||
import * as NOTIFICATION from 'actions/constants/notification';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ThunkAction,
|
|
||||||
AsyncAction,
|
|
||||||
PromiseAction,
|
PromiseAction,
|
||||||
PayloadAction,
|
|
||||||
GetState,
|
GetState,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
Network,
|
Network,
|
||||||
|
Account,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
import type { Discovery, State } from 'reducers/DiscoveryReducer';
|
import type { Discovery } from 'reducers/DiscoveryReducer';
|
||||||
import * as BlockchainActions from '../BlockchainActions';
|
|
||||||
|
|
||||||
export type DiscoveryStartAction = {
|
export type DiscoveryStartAction = {
|
||||||
type: typeof DISCOVERY.START,
|
type: typeof DISCOVERY.START,
|
||||||
device: TrezorDevice,
|
networkType: 'ripple',
|
||||||
network: Network,
|
network: Network,
|
||||||
publicKey: string,
|
|
||||||
chainCode: string,
|
|
||||||
basePath: Array<number>,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DiscoveryWaitingAction = {
|
|
||||||
type: typeof DISCOVERY.WAITING_FOR_DEVICE | typeof DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
|
||||||
device: TrezorDevice,
|
device: TrezorDevice,
|
||||||
network: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DiscoveryCompleteAction = {
|
|
||||||
type: typeof DISCOVERY.COMPLETE,
|
|
||||||
device: TrezorDevice,
|
|
||||||
network: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DiscoveryAction = {
|
|
||||||
type: typeof DISCOVERY.FROM_STORAGE,
|
|
||||||
payload: State
|
|
||||||
} | {
|
|
||||||
type: typeof DISCOVERY.STOP,
|
|
||||||
device: TrezorDevice
|
|
||||||
} | DiscoveryStartAction
|
|
||||||
| DiscoveryWaitingAction
|
|
||||||
| DiscoveryCompleteAction;
|
|
||||||
|
|
||||||
// There are multiple async methods during discovery process (trezor-connect and blockchain actions)
|
|
||||||
// This method will check after each of async action if process was interrupted (for example by network change or device disconnect)
|
|
||||||
const isProcessInterrupted = (process?: Discovery): PayloadAction<boolean> => (dispatch: Dispatch, getState: GetState): boolean => {
|
|
||||||
if (!process) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const { deviceState, network } = process;
|
|
||||||
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === deviceState && d.network === network);
|
|
||||||
if (!discoveryProcess) return false;
|
|
||||||
return discoveryProcess.interrupted;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Private action
|
export const begin = (device: TrezorDevice, network: Network): PromiseAction<DiscoveryStartAction> => async (): Promise<DiscoveryStartAction> => ({
|
||||||
// Called from "this.begin", "this.restore", "this.addAccount"
|
type: DISCOVERY.START,
|
||||||
const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
networkType: 'ripple',
|
||||||
const selected = getState().wallet.selectedDevice;
|
network,
|
||||||
if (!selected) {
|
device,
|
||||||
// TODO: throw error
|
});
|
||||||
console.error('Start discovery: no selected device', device);
|
|
||||||
return;
|
|
||||||
} if (selected.path !== device.path) {
|
|
||||||
console.error('Start discovery: requested device is not selected', device, selected);
|
|
||||||
return;
|
|
||||||
} if (!selected.state) {
|
|
||||||
console.warn("Start discovery: Selected device wasn't authenticated yet...");
|
|
||||||
return;
|
|
||||||
} if (selected.connected && !selected.available) {
|
|
||||||
console.warn('Start discovery: Selected device is unavailable...');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { discovery } = getState();
|
export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): PromiseAction<Account> => async (dispatch: Dispatch, getState: GetState): Promise<Account> => {
|
||||||
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
|
|
||||||
|
|
||||||
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
|
||||||
device,
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blockchain = getState().blockchain.find(b => b.shortcut === network);
|
|
||||||
if (blockchain && !blockchain.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
|
||||||
device,
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!discoveryProcess) {
|
|
||||||
dispatch(begin(device, network));
|
|
||||||
} else if (discoveryProcess.completed && !ignoreCompleted) {
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.COMPLETE,
|
|
||||||
device,
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice || discoveryProcess.waitingForBlockchain) {
|
|
||||||
// discovery cycle was interrupted
|
|
||||||
// start from beginning
|
|
||||||
dispatch(begin(device, network));
|
|
||||||
} else {
|
|
||||||
dispatch(discoverAccount(device, discoveryProcess));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// first iteration
|
|
||||||
// generate public key for this account
|
|
||||||
// start discovery process
|
|
||||||
const begin = (device: TrezorDevice, networkName: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const { config } = getState().localStorage;
|
|
||||||
const network = config.networks.find(c => c.shortcut === networkName);
|
|
||||||
if (!network) return;
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
|
||||||
device,
|
|
||||||
network: networkName,
|
|
||||||
});
|
|
||||||
|
|
||||||
// check for interruption
|
|
||||||
// corner case: DISCOVERY.START wasn't called yet, but Discovery exists in reducer created by DISCOVERY.WAITING_FOR_DEVICE action
|
|
||||||
// this is why we need to get process instance directly from reducer
|
|
||||||
const discoveryProcess = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
|
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
|
||||||
|
|
||||||
//const basePath: Array<number> = response.payload.path;
|
|
||||||
|
|
||||||
// send data to reducer
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.START,
|
|
||||||
network,
|
|
||||||
device,
|
|
||||||
publicKey: '',
|
|
||||||
chainCode: '',
|
|
||||||
basePath: [0, 0, 0],
|
|
||||||
});
|
|
||||||
|
|
||||||
// next iteration
|
|
||||||
dispatch(start(device, networkName));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const { config } = getState().localStorage;
|
const { config } = getState().localStorage;
|
||||||
const network = config.networks.find(c => c.shortcut === discoveryProcess.network);
|
const network = config.networks.find(c => c.shortcut === discoveryProcess.network);
|
||||||
if (!network) return;
|
if (!network) throw new Error('Discovery network not found');
|
||||||
|
|
||||||
const { completed, accountIndex } = discoveryProcess;
|
const { accountIndex } = discoveryProcess;
|
||||||
const path = network.bip44.slice(0).replace('a', accountIndex.toString());
|
const path = network.bip44.slice(0).replace('a', accountIndex.toString());
|
||||||
|
|
||||||
// $FlowIssue npm not released yet
|
// $FlowIssue npm not released yet
|
||||||
@ -180,127 +51,26 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// handle TREZOR response error
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
dispatch({
|
throw new Error(response.payload.error);
|
||||||
type: DISCOVERY.STOP,
|
|
||||||
device,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: NOTIFICATION.ADD,
|
|
||||||
payload: {
|
|
||||||
type: 'error',
|
|
||||||
title: 'Account discovery error',
|
|
||||||
message: response.payload.error,
|
|
||||||
cancelable: true,
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: 'Try again',
|
|
||||||
callback: () => {
|
|
||||||
dispatch(start(device, discoveryProcess.network));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
|
||||||
|
|
||||||
const account = response.payload;
|
const account = response.payload;
|
||||||
const accountIsEmpty = account.sequence <= 0 && account.balance === '0';
|
const empty = account.sequence <= 0 && account.balance === '0';
|
||||||
if (!accountIsEmpty || (accountIsEmpty && completed) || (accountIsEmpty && discoveryProcess.accountIndex === 0)) {
|
|
||||||
dispatch({
|
|
||||||
type: ACCOUNT.CREATE,
|
|
||||||
payload: {
|
|
||||||
index: discoveryProcess.accountIndex,
|
|
||||||
loaded: true,
|
|
||||||
network: network.shortcut,
|
|
||||||
deviceID: device.features ? device.features.device_id : '0',
|
|
||||||
deviceState: device.state || '0',
|
|
||||||
addressPath: account.path,
|
|
||||||
address: account.address,
|
|
||||||
balance: account.balance,
|
|
||||||
nonce: account.sequence,
|
|
||||||
block: account.block,
|
|
||||||
transactions: account.transactions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountIsEmpty) {
|
return {
|
||||||
dispatch(finish(device, discoveryProcess));
|
index: discoveryProcess.accountIndex,
|
||||||
} else if (!completed) {
|
loaded: true,
|
||||||
dispatch(discoverAccount(device, discoveryProcess));
|
network: network.shortcut,
|
||||||
}
|
deviceID: device.features ? device.features.device_id : '0',
|
||||||
};
|
deviceState: device.state || '0',
|
||||||
|
addressPath: account.path,
|
||||||
const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
address: account.address,
|
||||||
await TrezorConnect.getFeatures({
|
balance: account.balance,
|
||||||
device: {
|
nonce: account.sequence,
|
||||||
path: device.path,
|
block: account.block,
|
||||||
instance: device.instance,
|
transactions: account.transactions,
|
||||||
state: device.state,
|
empty,
|
||||||
},
|
};
|
||||||
keepSession: false,
|
};
|
||||||
// useEmptyPassphrase: !device.instance,
|
|
||||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
|
||||||
});
|
|
||||||
|
|
||||||
// await dispatch(BlockchainActions.subscribe(discoveryProcess.network));
|
|
||||||
|
|
||||||
if (dispatch(isProcessInterrupted(discoveryProcess))) return;
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.COMPLETE,
|
|
||||||
device,
|
|
||||||
network: discoveryProcess.network,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const reconnect = (network: string): PromiseAction<void> => async (dispatch: Dispatch): Promise<void> => {
|
|
||||||
await dispatch(BlockchainActions.subscribe(network));
|
|
||||||
dispatch(restore());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Called after DEVICE.CONNECT ('trezor-connect') or CONNECT.AUTH_DEVICE actions in WalletService
|
|
||||||
// OR after BlockchainSubscribe in this.reconnect
|
|
||||||
export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
// check if current url has "network" parameter
|
|
||||||
const urlParams = getState().router.location.state;
|
|
||||||
if (!urlParams.network) return;
|
|
||||||
|
|
||||||
// make sure that "selectedDevice" exists
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
|
||||||
if (!selected) return;
|
|
||||||
|
|
||||||
// find discovery process for requested network
|
|
||||||
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.network === urlParams.network);
|
|
||||||
|
|
||||||
// if there was no process befor OR process was interrupted/waiting
|
|
||||||
const shouldStart = !discoveryProcess || (discoveryProcess.interrupted || discoveryProcess.waitingForDevice || discoveryProcess.waitingForBlockchain);
|
|
||||||
if (shouldStart) {
|
|
||||||
dispatch(start(selected, urlParams.network));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const stop = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const device: ?TrezorDevice = getState().wallet.selectedDevice;
|
|
||||||
if (!device) return;
|
|
||||||
|
|
||||||
// get all uncompleted discovery processes which assigned to selected device
|
|
||||||
const discoveryProcesses = getState().discovery.filter(d => d.deviceState === device.state && !d.completed);
|
|
||||||
if (discoveryProcesses.length > 0) {
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.STOP,
|
|
||||||
device,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addAccount = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
|
||||||
if (!selected) return;
|
|
||||||
dispatch(start(selected, getState().router.location.state.network, true));
|
|
||||||
};
|
|
@ -23,6 +23,7 @@ export type Account = {
|
|||||||
nonce: number;
|
nonce: number;
|
||||||
block: number;
|
block: number;
|
||||||
transactions: number;
|
transactions: number;
|
||||||
|
empty: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type State = Array<Account>;
|
export type State = Array<Account>;
|
||||||
|
Loading…
Reference in New Issue
Block a user