2017-12-13 11:01:37 +00:00
|
|
|
/* @flow */
|
2018-07-30 10:52:13 +00:00
|
|
|
import TrezorConnect, {
|
2019-03-04 12:33:02 +00:00
|
|
|
DEVICE,
|
|
|
|
DEVICE_EVENT,
|
|
|
|
UI_EVENT,
|
|
|
|
TRANSPORT_EVENT,
|
|
|
|
BLOCKCHAIN_EVENT,
|
2018-07-30 10:52:13 +00:00
|
|
|
} from 'trezor-connect';
|
2018-10-10 19:27:07 +00:00
|
|
|
import { CONTEXT_NONE } from 'actions/constants/modal';
|
2019-02-26 12:40:22 +00:00
|
|
|
import urlConstants from 'constants/urls';
|
2018-08-14 13:18:16 +00:00
|
|
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
|
|
|
import * as NOTIFICATION from 'actions/constants/notification';
|
2018-08-20 11:01:43 +00:00
|
|
|
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
2018-09-20 16:26:45 +00:00
|
|
|
import * as RouterActions from 'actions/RouterActions';
|
2018-10-05 15:55:26 +00:00
|
|
|
import * as deviceUtils from 'utils/device';
|
2018-10-11 16:04:25 +00:00
|
|
|
import * as buildUtils from 'utils/build';
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
import type {
|
|
|
|
DeviceMessage,
|
2018-09-12 11:25:21 +00:00
|
|
|
DeviceMessageType,
|
2018-04-16 21:19:50 +00:00
|
|
|
UiMessage,
|
2018-09-12 11:25:21 +00:00
|
|
|
UiMessageType,
|
2018-04-16 21:19:50 +00:00
|
|
|
TransportMessage,
|
|
|
|
TransportMessageType,
|
2018-11-29 20:08:41 +00:00
|
|
|
BlockchainEvent,
|
2018-04-16 21:19:50 +00:00
|
|
|
} from 'trezor-connect';
|
|
|
|
|
2018-07-30 10:52:13 +00:00
|
|
|
import type {
|
2018-04-16 21:19:50 +00:00
|
|
|
Dispatch,
|
|
|
|
GetState,
|
|
|
|
Action,
|
2018-05-02 09:01:08 +00:00
|
|
|
ThunkAction,
|
2018-04-16 21:19:50 +00:00
|
|
|
AsyncAction,
|
2018-08-17 13:24:47 +00:00
|
|
|
Device,
|
2018-04-16 21:19:50 +00:00
|
|
|
TrezorDevice,
|
2018-08-14 12:56:47 +00:00
|
|
|
} from 'flowtype';
|
2018-04-16 21:19:50 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export type TrezorConnectAction =
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.INITIALIZATION_ERROR,
|
|
|
|
error: string,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.NETWORK_CHANGED,
|
|
|
|
payload: {
|
|
|
|
network: string,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.AUTH_DEVICE,
|
|
|
|
device: TrezorDevice,
|
|
|
|
state: string,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.DUPLICATE,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.REMEMBER_REQUEST,
|
|
|
|
device: TrezorDevice,
|
|
|
|
instances: Array<TrezorDevice>,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.DISCONNECT_REQUEST,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.FORGET_REQUEST,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.FORGET,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.FORGET_SINGLE | typeof CONNECT.FORGET_SILENT,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.REMEMBER,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.TRY_TO_DUPLICATE,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.DEVICE_FROM_STORAGE,
|
|
|
|
payload: Array<TrezorDevice>,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.START_ACQUIRING | typeof CONNECT.STOP_ACQUIRING,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.REQUEST_WALLET_TYPE,
|
|
|
|
device: TrezorDevice,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof CONNECT.RECEIVE_WALLET_TYPE | typeof CONNECT.UPDATE_WALLET_TYPE,
|
|
|
|
device: TrezorDevice,
|
|
|
|
hidden: boolean,
|
|
|
|
};
|
2018-04-16 21:19:50 +00:00
|
|
|
|
2018-10-10 13:45:21 +00:00
|
|
|
declare var LOCAL: ?string;
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const init = (): AsyncAction => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-07-30 10:52:13 +00:00
|
|
|
// set listeners
|
2019-03-04 12:33:02 +00:00
|
|
|
TrezorConnect.on(
|
|
|
|
DEVICE_EVENT,
|
|
|
|
(event: DeviceMessage): void => {
|
|
|
|
// post event to reducers
|
|
|
|
const type: DeviceMessageType = event.type; // eslint-disable-line prefer-destructuring
|
|
|
|
dispatch({
|
|
|
|
type,
|
|
|
|
device: event.payload,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
TrezorConnect.on(
|
|
|
|
UI_EVENT,
|
|
|
|
(event: UiMessage): void => {
|
|
|
|
// post event to reducers
|
|
|
|
const type: UiMessageType = event.type; // eslint-disable-line prefer-destructuring
|
|
|
|
dispatch({
|
|
|
|
type,
|
|
|
|
payload: event.payload,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2018-03-27 15:12:01 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
TrezorConnect.on(
|
|
|
|
TRANSPORT_EVENT,
|
|
|
|
(event: TransportMessage): void => {
|
|
|
|
// post event to reducers
|
|
|
|
const type: TransportMessageType = event.type; // eslint-disable-line prefer-destructuring
|
|
|
|
dispatch({
|
|
|
|
type,
|
|
|
|
payload: event.payload,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-11-29 20:08:41 +00:00
|
|
|
// post event to reducers
|
2019-03-04 12:33:02 +00:00
|
|
|
TrezorConnect.on(
|
|
|
|
BLOCKCHAIN_EVENT,
|
|
|
|
(event: BlockchainEvent): void => {
|
|
|
|
dispatch(event);
|
|
|
|
}
|
|
|
|
);
|
2018-09-12 11:25:21 +00:00
|
|
|
|
2018-10-11 16:04:25 +00:00
|
|
|
if (buildUtils.isDev()) {
|
2019-03-04 12:33:02 +00:00
|
|
|
// eslint-disable-next-line
|
|
|
|
window.__TREZOR_CONNECT_SRC =
|
2019-04-11 14:00:57 +00:00
|
|
|
typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/'; // eslint-disable-line no-underscore-dangle
|
2019-02-26 11:37:26 +00:00
|
|
|
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://localhost:8088/'; // eslint-disable-line no-underscore-dangle
|
2018-10-10 13:46:42 +00:00
|
|
|
window.TrezorConnect = TrezorConnect;
|
|
|
|
}
|
2018-08-08 11:21:28 +00:00
|
|
|
|
2018-07-30 10:52:13 +00:00
|
|
|
try {
|
|
|
|
await TrezorConnect.init({
|
|
|
|
transportReconnect: true,
|
2018-09-20 16:26:45 +00:00
|
|
|
debug: false,
|
2018-07-30 10:52:13 +00:00
|
|
|
popup: false,
|
|
|
|
webusb: true,
|
2019-03-04 12:33:02 +00:00
|
|
|
pendingTransportEvent: getState().devices.length < 1,
|
2019-02-26 11:37:26 +00:00
|
|
|
manifest: {
|
|
|
|
email: 'info@trezor.io',
|
2019-02-26 12:40:22 +00:00
|
|
|
appUrl: urlConstants.NEXT_WALLET,
|
2019-02-26 11:37:26 +00:00
|
|
|
},
|
2018-07-30 10:52:13 +00:00
|
|
|
});
|
|
|
|
} catch (error) {
|
2018-10-02 08:05:51 +00:00
|
|
|
dispatch({
|
|
|
|
type: CONNECT.INITIALIZATION_ERROR,
|
|
|
|
error,
|
|
|
|
});
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-09-06 15:04:28 +00:00
|
|
|
// called after backend was initialized
|
|
|
|
// set listeners for connect/disconnect
|
2018-09-21 12:01:41 +00:00
|
|
|
export const postInit = (): ThunkAction => (dispatch: Dispatch): void => {
|
2018-09-06 15:04:28 +00:00
|
|
|
const handleDeviceConnect = (device: Device) => {
|
2018-09-21 12:01:41 +00:00
|
|
|
dispatch(RouterActions.selectDevice(device));
|
2018-09-06 15:04:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
|
|
|
|
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
|
|
|
|
|
|
|
|
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
|
|
|
|
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
|
|
|
|
|
2018-09-20 16:26:45 +00:00
|
|
|
// try to redirect to initial url
|
2018-09-21 12:01:41 +00:00
|
|
|
if (!dispatch(RouterActions.setInitialUrl())) {
|
2018-09-20 16:26:45 +00:00
|
|
|
// if initial redirection fails try to switch to first available device
|
2018-09-21 12:01:41 +00:00
|
|
|
dispatch(RouterActions.selectFirstAvailableDevice());
|
2018-09-06 15:04:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const requestWalletType = (): AsyncAction => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-10-05 13:47:37 +00:00
|
|
|
const selected = getState().wallet.selectedDevice;
|
|
|
|
if (!selected) return;
|
2019-03-04 12:33:02 +00:00
|
|
|
const isDeviceReady =
|
|
|
|
selected.connected &&
|
|
|
|
selected.features &&
|
|
|
|
!selected.state &&
|
|
|
|
selected.mode === 'normal' &&
|
|
|
|
selected.firmware !== 'required';
|
2018-10-05 13:47:37 +00:00
|
|
|
if (!isDeviceReady) return;
|
|
|
|
|
2018-10-09 12:39:10 +00:00
|
|
|
if (selected.features && selected.features.passphrase_protection) {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.REQUEST_WALLET_TYPE,
|
|
|
|
device: selected,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.RECEIVE_WALLET_TYPE,
|
|
|
|
device: selected,
|
|
|
|
hidden: false,
|
|
|
|
state: selected.state,
|
|
|
|
});
|
|
|
|
}
|
2018-10-05 13:47:37 +00:00
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const authorizeDevice = (): AsyncAction => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-07-30 10:52:13 +00:00
|
|
|
const selected = getState().wallet.selectedDevice;
|
2018-10-04 20:59:39 +00:00
|
|
|
if (!selected) return;
|
2019-03-04 12:33:02 +00:00
|
|
|
const isDeviceReady =
|
|
|
|
selected.connected &&
|
|
|
|
selected.features &&
|
|
|
|
!selected.state &&
|
|
|
|
selected.mode === 'normal' &&
|
|
|
|
selected.firmware !== 'required';
|
2018-10-04 20:59:39 +00:00
|
|
|
if (!isDeviceReady) return;
|
|
|
|
|
|
|
|
const response = await TrezorConnect.getDeviceState({
|
|
|
|
device: {
|
|
|
|
path: selected.path,
|
|
|
|
instance: selected.instance,
|
|
|
|
state: selected.state,
|
|
|
|
},
|
2018-10-05 13:47:37 +00:00
|
|
|
useEmptyPassphrase: selected.useEmptyPassphrase,
|
2018-10-04 20:59:39 +00:00
|
|
|
});
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-10-04 20:59:39 +00:00
|
|
|
if (response && response.success) {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.AUTH_DEVICE,
|
|
|
|
device: selected,
|
|
|
|
state: response.payload.state,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
devicePath: selected.path,
|
|
|
|
type: 'error',
|
|
|
|
title: 'Authentication error',
|
|
|
|
message: response.payload.error,
|
|
|
|
cancelable: false,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.CLOSE,
|
|
|
|
payload: { devicePath: selected.path },
|
|
|
|
});
|
2018-10-05 13:47:37 +00:00
|
|
|
dispatch(authorizeDevice());
|
2018-07-30 10:52:13 +00:00
|
|
|
},
|
2018-10-04 20:59:39 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
});
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const deviceDisconnect = (device: Device): AsyncAction => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-10-01 11:21:58 +00:00
|
|
|
if (device.features) {
|
2019-03-04 12:33:02 +00:00
|
|
|
const instances = getState().devices.filter(
|
|
|
|
d =>
|
|
|
|
d.features &&
|
|
|
|
device.features &&
|
|
|
|
d.state &&
|
|
|
|
!d.remember &&
|
|
|
|
d.features.device_id === device.features.device_id
|
|
|
|
);
|
2018-07-30 10:52:13 +00:00
|
|
|
if (instances.length > 0) {
|
2019-03-04 12:33:02 +00:00
|
|
|
const isSelected = deviceUtils.isSelectedDevice(
|
|
|
|
getState().wallet.selectedDevice,
|
|
|
|
device
|
|
|
|
);
|
2018-10-10 19:27:07 +00:00
|
|
|
if (!isSelected && getState().modal.context !== CONTEXT_NONE) {
|
2018-10-05 15:55:26 +00:00
|
|
|
dispatch({
|
|
|
|
type: CONNECT.FORGET_SILENT,
|
|
|
|
device: instances[0],
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.REMEMBER_REQUEST,
|
|
|
|
device: instances[0],
|
|
|
|
instances,
|
|
|
|
});
|
|
|
|
}
|
2018-10-01 11:21:58 +00:00
|
|
|
} else {
|
|
|
|
dispatch(RouterActions.selectFirstAvailableDevice());
|
2018-03-08 16:10:53 +00:00
|
|
|
}
|
2018-10-01 11:21:58 +00:00
|
|
|
} else {
|
|
|
|
dispatch(RouterActions.selectFirstAvailableDevice());
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
export function reload(): AsyncAction {
|
2019-03-04 12:33:02 +00:00
|
|
|
return async (): Promise<void> => {};
|
2018-04-11 10:06:46 +00:00
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
export function acquire(): AsyncAction {
|
|
|
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
2018-05-22 20:07:25 +00:00
|
|
|
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
2018-04-16 21:19:50 +00:00
|
|
|
if (!selected) return;
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-05-23 09:46:57 +00:00
|
|
|
dispatch({
|
|
|
|
type: CONNECT.START_ACQUIRING,
|
2018-07-30 10:52:13 +00:00
|
|
|
});
|
2018-05-23 09:46:57 +00:00
|
|
|
|
2018-10-05 13:47:37 +00:00
|
|
|
// this is the only place where useEmptyPassphrase should be used every time
|
|
|
|
// the goal here is to acquire device and get his features
|
|
|
|
// authentication (passphrase) is not needed here yet
|
2018-07-30 10:52:13 +00:00
|
|
|
const response = await TrezorConnect.getFeatures({
|
2018-02-20 09:30:36 +00:00
|
|
|
device: {
|
|
|
|
path: selected.path,
|
2018-05-22 10:54:30 +00:00
|
|
|
},
|
2018-10-05 13:47:37 +00:00
|
|
|
useEmptyPassphrase: true,
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
|
|
|
|
2018-05-16 16:55:12 +00:00
|
|
|
if (!response.success) {
|
2018-02-20 09:30:36 +00:00
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
type: 'error',
|
|
|
|
title: 'Acquire device error',
|
2018-03-27 15:12:01 +00:00
|
|
|
message: response.payload.error,
|
2018-02-20 09:30:36 +00:00
|
|
|
cancelable: true,
|
2018-05-23 09:46:57 +00:00
|
|
|
// actions: [
|
|
|
|
// {
|
|
|
|
// label: 'Try again',
|
|
|
|
// callback: () => {
|
|
|
|
// dispatch(acquire())
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// ]
|
2018-07-30 10:52:13 +00:00
|
|
|
},
|
|
|
|
});
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
2018-05-23 09:46:57 +00:00
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.STOP_ACQUIRING,
|
2018-07-30 10:52:13 +00:00
|
|
|
});
|
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from Aside - device menu (forget single instance)
|
2018-07-30 10:52:13 +00:00
|
|
|
export const forget = (device: TrezorDevice): Action => ({
|
|
|
|
type: CONNECT.FORGET_REQUEST,
|
|
|
|
device,
|
|
|
|
});
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const duplicateDeviceOld = (device: TrezorDevice): AsyncAction => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-08-17 13:20:42 +00:00
|
|
|
const instance: number = getDuplicateInstanceNumber(getState().devices, device);
|
2018-08-16 20:59:41 +00:00
|
|
|
const extended: Object = { instance };
|
2018-07-30 10:52:13 +00:00
|
|
|
dispatch({
|
2018-08-16 20:59:41 +00:00
|
|
|
type: CONNECT.DUPLICATE,
|
2018-08-20 11:01:43 +00:00
|
|
|
device: { ...device, ...extended },
|
2018-07-30 10:52:13 +00:00
|
|
|
});
|
|
|
|
};
|
2018-10-05 13:47:37 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (
|
|
|
|
dispatch: Dispatch
|
|
|
|
): Promise<void> => {
|
2018-10-05 13:47:37 +00:00
|
|
|
dispatch({
|
|
|
|
type: CONNECT.REQUEST_WALLET_TYPE,
|
|
|
|
device,
|
|
|
|
});
|
|
|
|
};
|