2017-12-13 11:01:37 +00:00
|
|
|
/* @flow */
|
|
|
|
'use strict';
|
|
|
|
|
2018-03-27 15:12:01 +00:00
|
|
|
import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT } from 'trezor-connect';
|
2017-12-13 11:01:37 +00:00
|
|
|
import * as ACTIONS from './index';
|
2018-02-20 09:30:36 +00:00
|
|
|
import * as ADDRESS from './constants/Address';
|
|
|
|
import * as TOKEN from './constants/Token';
|
|
|
|
import * as CONNECT from './constants/TrezorConnect';
|
|
|
|
import * as DISCOVERY from './constants/Discovery';
|
|
|
|
import * as NOTIFICATION from './constants/notification';
|
2017-12-13 11:01:37 +00:00
|
|
|
|
|
|
|
import HDKey from 'hdkey';
|
|
|
|
import EthereumjsUtil from 'ethereumjs-util';
|
|
|
|
import EthereumjsTx from 'ethereumjs-tx';
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
//import { getBalance } from '../services/Web3Service';
|
|
|
|
import { getBalance } from './Web3Actions';
|
2017-12-13 11:01:37 +00:00
|
|
|
import { getTransactionHistory } from '../services/EtherscanService';
|
|
|
|
|
|
|
|
import { push } from 'react-router-redux';
|
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
import { init as initWeb3, getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
import type { Discovery } from '../reducers/DiscoveryReducer';
|
|
|
|
import { resolveAfter } from '../utils/promiseUtils';
|
|
|
|
import { getAccounts } from '../utils/reducerUtils';
|
|
|
|
import { findSelectedDevice, isSavedDevice } from '../reducers/TrezorConnectReducer';
|
|
|
|
|
|
|
|
export const init = (): any => {
|
|
|
|
return async (dispatch, getState): Promise<void> => {
|
|
|
|
// set listeners
|
|
|
|
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
|
|
|
|
dispatch({
|
|
|
|
type: event.type,
|
2018-03-27 15:12:01 +00:00
|
|
|
device: event.payload
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const version: Object = TrezorConnect.getVersion();
|
2018-03-27 15:12:01 +00:00
|
|
|
TrezorConnect.on(UI_EVENT, (event: UiMessage): void => {
|
|
|
|
// post event to reducers
|
|
|
|
dispatch({
|
|
|
|
type: event.type,
|
|
|
|
payload: event.payload
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
2018-03-27 15:12:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
TrezorConnect.on(TRANSPORT_EVENT, (event: UiMessage): void => {
|
|
|
|
// post event to reducers
|
|
|
|
dispatch({
|
|
|
|
type: event.type,
|
|
|
|
payload: event.payload
|
|
|
|
});
|
|
|
|
});
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
await TrezorConnect.init({
|
2018-03-27 15:12:01 +00:00
|
|
|
// transportReconnect: true,
|
|
|
|
coinsSrc: './data/coins.json',
|
|
|
|
firmwareReleasesSrc: './data/releases-1.json',
|
|
|
|
transportConfigSrc: './data/config_signed.bin',
|
|
|
|
transportReconnect: false,
|
|
|
|
latestBridgeSrc: './data/latest.txt',
|
|
|
|
connectSrc: 'https://localhost:8088/',
|
|
|
|
// connectSrc: 'https://sisyfos.trezor.io/',
|
|
|
|
debug: true,
|
|
|
|
popup: false,
|
|
|
|
// webusb: false
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
dispatch( initWeb3() );
|
|
|
|
}, 2000)
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.INITIALIZATION_ERROR,
|
|
|
|
error
|
|
|
|
})
|
|
|
|
}
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
// called after backend was initialized
|
|
|
|
// set listeners for connect/disconnect
|
|
|
|
export const postInit = (): any => {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
const handleDeviceConnect = (device) => {
|
|
|
|
dispatch( initConnectedDevice(device) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// const handleDeviceDisconnect = (device) => {
|
|
|
|
// // remove addresses and discovery from state
|
|
|
|
// // dispatch( remove(device) );
|
|
|
|
// }
|
|
|
|
|
|
|
|
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
|
|
|
|
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
|
|
|
|
|
|
|
|
// TrezorConnect.on(DEVICE.DISCONNECT, handleDeviceDisconnect);
|
|
|
|
// TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceDisconnect);
|
|
|
|
|
|
|
|
// possible race condition:
|
|
|
|
// devices were connected before Web3 initialized. force DEVICE.CONNECT event on them
|
|
|
|
const { devices } = getState().connect;
|
|
|
|
|
|
|
|
if (devices.length > 0) {
|
|
|
|
const unacquired = devices.find(d => d.unacquired);
|
|
|
|
if (unacquired) {
|
|
|
|
handleDeviceConnect(unacquired);
|
|
|
|
} else {
|
|
|
|
const latest = devices.sort((a, b) => {
|
|
|
|
if (!a.ts || !b.ts) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return a.ts > b.ts ? 1 : -1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log("LATEST", latest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let d of devices) {
|
|
|
|
handleDeviceConnect(d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const initConnectedDevice = (device: any): any => {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
//dispatch( onSelectDevice(device) );
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const selected = findSelectedDevice(getState().connect);
|
2018-03-08 16:10:53 +00:00
|
|
|
if (selected && selected.checksum) {
|
2018-02-20 09:30:36 +00:00
|
|
|
dispatch( onSelectDevice(device) );
|
|
|
|
} else if (!selected) {
|
|
|
|
dispatch( onSelectDevice(device) );
|
|
|
|
}
|
2018-03-08 16:10:53 +00:00
|
|
|
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
|
|
|
|
// dispatch( onSelectDevice(device) );
|
|
|
|
// } else if (!selected) {
|
|
|
|
// dispatch( onSelectDevice(device) );
|
|
|
|
// }
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// selection from Aside dropdown
|
|
|
|
// or after acquiring device
|
|
|
|
export function onSelectDevice(device: any): any {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
// || device.isUsedElsewhere
|
|
|
|
if (device.unacquired) {
|
|
|
|
dispatch( push(`/device/${ device.path }/acquire`) );
|
|
|
|
} else if (device.features.bootloader_mode) {
|
|
|
|
dispatch( push(`/device/${ device.path }/bootloader`) );
|
|
|
|
} else if (device.instance) {
|
|
|
|
dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) );
|
|
|
|
} else {
|
|
|
|
//dispatch( push(`/device/${ device.features.device_id }/coin/etc/address/0`) );
|
|
|
|
dispatch( push(`/device/${ device.features.device_id }`) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: as TrezorConnect method
|
|
|
|
const __getDeviceState = async (path, instance): Promise<any> => {
|
|
|
|
return await TrezorConnect.getPublicKey({
|
|
|
|
device: {
|
|
|
|
path,
|
|
|
|
instance
|
|
|
|
},
|
|
|
|
// selectedDevice: path,
|
|
|
|
instance: instance,
|
|
|
|
path: "m/1'/0'/0'",
|
|
|
|
confirmation: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export const switchToFirstAvailableDevice = (): any => {
|
|
|
|
return async (dispatch, getState): Promise<void> => {
|
|
|
|
|
|
|
|
const { devices } = getState().connect;
|
|
|
|
if (devices.length > 0) {
|
|
|
|
// TODO: Priority:
|
|
|
|
// 1. Unacquired
|
|
|
|
// 2. First connected
|
|
|
|
// 3. Saved with latest timestamp
|
|
|
|
// 4. First from the list
|
|
|
|
const unacquired = devices.find(d => d.unacquired);
|
|
|
|
if (unacquired) {
|
|
|
|
dispatch( initConnectedDevice(unacquired) );
|
|
|
|
} else {
|
|
|
|
const latest = devices.sort((a, b) => {
|
|
|
|
if (!a.ts || !b.ts) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return a.ts > b.ts ? 1 : -1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
dispatch( initConnectedDevice(devices[0]) );
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
dispatch( push('/') );
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.SELECT_DEVICE,
|
|
|
|
payload: null
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const getSelectedDeviceState = (): any => {
|
|
|
|
return async (dispatch, getState): Promise<void> => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
console.warn("init selected", selected)
|
|
|
|
if (selected
|
|
|
|
&& selected.connected
|
|
|
|
&& !selected.unacquired
|
|
|
|
&& !selected.acquiring
|
|
|
|
&& !selected.checksum) {
|
|
|
|
|
|
|
|
const response = await __getDeviceState(selected.path, selected.instance);
|
|
|
|
|
|
|
|
if (response && response.success) {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.AUTH_DEVICE,
|
|
|
|
device: selected,
|
2018-03-27 15:12:01 +00:00
|
|
|
checksum: response.payload.xpub
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
type: 'error',
|
|
|
|
title: 'Authentification error',
|
2018-03-27 15:12:01 +00:00
|
|
|
message: response.payload.error,
|
2018-02-20 09:30:36 +00:00
|
|
|
cancelable: true,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch( getSelectedDeviceState() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const deviceDisconnect = (device: any): any => {
|
|
|
|
return async (dispatch, getState): Promise<void> => {
|
|
|
|
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
if (device && device.features) {
|
|
|
|
if (selected && selected.features.device_id === device.features.device_id) {
|
|
|
|
stopDiscoveryProcess(selected);
|
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id);
|
|
|
|
if (affected.length > 0) {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.REMEMBER_REQUEST,
|
|
|
|
device,
|
|
|
|
allInstances: affected
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
if (!selected) {
|
|
|
|
dispatch( switchToFirstAvailableDevice() );
|
|
|
|
}
|
2018-03-08 16:10:53 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const coinChanged = (coin: ?string): any => {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
dispatch( stopDiscoveryProcess(selected) );
|
|
|
|
|
|
|
|
if (coin) {
|
|
|
|
dispatch( startDiscoveryProcess(selected, coin) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function acquire(): any {
|
2017-12-13 11:01:37 +00:00
|
|
|
return async (dispatch, getState) => {
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
|
|
|
|
const saved = getState().connect.devices.map(d => {
|
|
|
|
if (d.checksum) {
|
|
|
|
return {
|
|
|
|
instance: d.instance,
|
|
|
|
checksum: d.checksum
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
//const response = await __acquire(selected.path, selected.instance);
|
2017-12-13 11:01:37 +00:00
|
|
|
|
|
|
|
dispatch({
|
2018-02-20 09:30:36 +00:00
|
|
|
type: CONNECT.START_ACQUIRING,
|
|
|
|
device: selected
|
|
|
|
});
|
|
|
|
|
|
|
|
const response = await TrezorConnect.getFeatures({
|
|
|
|
device: {
|
|
|
|
path: selected.path,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const selected2 = findSelectedDevice(getState().connect);
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.STOP_ACQUIRING,
|
|
|
|
device: selected2
|
|
|
|
});
|
|
|
|
|
|
|
|
if (response && response.success) {
|
|
|
|
dispatch({
|
|
|
|
type: DEVICE.ACQUIRED,
|
|
|
|
// checksum: response
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// TODO: handle invalid pin?
|
|
|
|
console.log("-errror ack", response)
|
|
|
|
|
|
|
|
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,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch(acquire())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const forgetDevice = (device: any) => {
|
|
|
|
return (dispatch: any, getState: any): any => {
|
|
|
|
|
|
|
|
// find accounts associated with this device
|
|
|
|
const accounts: Array<any> = getState().accounts.find(a => a.checksum === device.checksum);
|
|
|
|
|
|
|
|
|
|
|
|
// find discovery processes associated with this device
|
|
|
|
const discovery: Array<any> = getState().discovery.find(d => d.checksum === device.checksum);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// called from Aside - device menu (forget single instance)
|
|
|
|
export const forget = (device: any) => {
|
|
|
|
return {
|
|
|
|
type: CONNECT.FORGET_REQUEST,
|
|
|
|
device
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export const duplicateDevice = (device: any) => {
|
|
|
|
return async (dispatch: any, getState: any): Promise<void> => {
|
|
|
|
dispatch({
|
|
|
|
type: CONNECT.TRY_TO_DUPLICATE,
|
|
|
|
device
|
2017-12-13 11:01:37 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
export const onDuplicateDevice = () => {
|
|
|
|
return async (dispatch: any, getState: any): Promise<void> => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
dispatch(onSelectDevice(selected));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const beginDiscoveryProcess = (device: any, coin: string): any => {
|
2017-12-13 11:01:37 +00:00
|
|
|
return async (dispatch, getState) => {
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const { config } = getState().localStorage;
|
2018-03-08 16:10:53 +00:00
|
|
|
const coinToDiscover = config.coins.find(c => c.network === coin);
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
// TODO: validate device checksum
|
|
|
|
// const checksum = await __acquire(device.path, device.instance);
|
|
|
|
// if (checksum && checksum.success) {
|
2018-03-27 15:12:01 +00:00
|
|
|
// if (checksum.payload.xpub !== device.checksum) {
|
2018-02-20 09:30:36 +00:00
|
|
|
// console.error("Incorrect checksum!");
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// get xpub from TREZOR
|
|
|
|
const response = await TrezorConnect.getPublicKey({
|
|
|
|
device: {
|
|
|
|
path: device.path,
|
|
|
|
instance: device.instance,
|
|
|
|
state: device.checksum
|
|
|
|
},
|
|
|
|
path: coinToDiscover.bip44,
|
|
|
|
confirmation: false,
|
2018-03-27 15:12:01 +00:00
|
|
|
keepSession: true // acquire and hold session
|
2018-02-20 09:30:36 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!response.success) {
|
|
|
|
// TODO: check message
|
|
|
|
console.warn("DISCO ERROR", response)
|
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
type: 'error',
|
|
|
|
title: 'Discovery error',
|
2018-03-27 15:12:01 +00:00
|
|
|
message: response.payload.error,
|
2018-02-20 09:30:36 +00:00
|
|
|
cancelable: true,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch(startDiscoveryProcess(device, coin))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return;
|
|
|
|
}
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
// TODO: check for interruption
|
|
|
|
|
|
|
|
// TODO: handle response error
|
2018-03-27 15:12:01 +00:00
|
|
|
const basePath: Array<number> = response.payload.path;
|
2017-12-13 11:01:37 +00:00
|
|
|
const hdKey = new HDKey();
|
2018-03-27 15:12:01 +00:00
|
|
|
hdKey.publicKey = new Buffer(response.payload.publicKey, 'hex');
|
|
|
|
hdKey.chainCode = new Buffer(response.payload.chainCode, 'hex');
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
// send data to reducer
|
|
|
|
dispatch({
|
|
|
|
type: DISCOVERY.START,
|
2018-03-08 16:10:53 +00:00
|
|
|
coin: coinToDiscover.network,
|
2018-02-20 09:30:36 +00:00
|
|
|
device,
|
2018-03-27 15:12:01 +00:00
|
|
|
xpub: response.payload.publicKey,
|
2018-02-20 09:30:36 +00:00
|
|
|
basePath,
|
|
|
|
hdKey,
|
|
|
|
});
|
|
|
|
|
|
|
|
dispatch( startDiscoveryProcess(device, coin) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const discoverAddress = (device: any, discoveryProcess: Discovery): any => {
|
|
|
|
return async (dispatch, getState) => {
|
|
|
|
|
|
|
|
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 coin = discoveryProcess.coin;
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: ADDRESS.CREATE,
|
|
|
|
device,
|
|
|
|
coin,
|
|
|
|
index: discoveryProcess.accountIndex,
|
|
|
|
path,
|
|
|
|
address: ethAddress
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: check if address was created before
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
// verify address with TREZOR
|
|
|
|
const verifyAddress = await TrezorConnect.ethereumGetAddress({
|
|
|
|
device: {
|
|
|
|
path: device.path,
|
|
|
|
instance: device.instance,
|
|
|
|
state: device.checksum
|
|
|
|
},
|
2018-03-27 15:12:01 +00:00
|
|
|
path,
|
2018-02-20 09:30:36 +00:00
|
|
|
showOnTrezor: false
|
|
|
|
});
|
|
|
|
if (discoveryProcess.interrupted) return;
|
|
|
|
|
|
|
|
if (verifyAddress && verifyAddress.success) {
|
2018-03-27 15:12:01 +00:00
|
|
|
//const trezorAddress: string = '0x' + verifyAddress.payload.address;
|
|
|
|
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
|
2018-02-20 09:30:36 +00:00
|
|
|
if (trezorAddress !== ethAddress) {
|
|
|
|
// throw inconsistent state error
|
|
|
|
console.warn("Inconsistent state", trezorAddress, ethAddress);
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
type: 'error',
|
|
|
|
title: 'Address validation error',
|
|
|
|
message: `Addresses are different. ${ trezorAddress } : ${ ethAddress }`,
|
|
|
|
cancelable: true,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch(startDiscoveryProcess(device, discoveryProcess.coin))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// handle TREZOR communication error
|
2017-12-13 11:01:37 +00:00
|
|
|
dispatch({
|
2018-02-20 09:30:36 +00:00
|
|
|
type: NOTIFICATION.ADD,
|
|
|
|
payload: {
|
|
|
|
type: 'error',
|
|
|
|
title: 'Address validation error',
|
2018-03-27 15:12:01 +00:00
|
|
|
message: verifyAddress.payload.error,
|
2018-02-20 09:30:36 +00:00
|
|
|
cancelable: true,
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
label: 'Try again',
|
|
|
|
callback: () => {
|
|
|
|
dispatch(startDiscoveryProcess(device, discoveryProcess.coin))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const web3instance = getState().web3.find(w3 => w3.coin === coin);
|
|
|
|
|
|
|
|
const balance = await getBalanceAsync(web3instance.web3, ethAddress);
|
|
|
|
if (discoveryProcess.interrupted) return;
|
|
|
|
dispatch({
|
|
|
|
type: ADDRESS.SET_BALANCE,
|
|
|
|
address: ethAddress,
|
|
|
|
balance: web3instance.web3.fromWei(balance.toString(), 'ether')
|
|
|
|
});
|
|
|
|
|
|
|
|
const userTokens = [];
|
|
|
|
// const userTokens = [
|
|
|
|
// { symbol: 'T01', address: '0x58cda554935e4a1f2acbe15f8757400af275e084' },
|
|
|
|
// { symbol: 'Lahod', address: '0x3360d0ee34a49d9ac34dce88b000a2903f2806ee' },
|
|
|
|
// ];
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
for (let i = 0; i < userTokens.length; i++) {
|
|
|
|
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, userTokens[i].address, ethAddress);
|
|
|
|
if (discoveryProcess.interrupted) return;
|
2017-12-13 11:01:37 +00:00
|
|
|
dispatch({
|
2018-02-20 09:30:36 +00:00
|
|
|
type: TOKEN.SET_BALANCE,
|
|
|
|
tokenName: userTokens[i].symbol,
|
|
|
|
ethAddress: ethAddress,
|
|
|
|
tokenAddress: userTokens[i].address,
|
|
|
|
balance: tokenBalance.toString()
|
2017-12-13 11:01:37 +00:00
|
|
|
})
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
const nonce = await getNonceAsync(web3instance.web3, ethAddress);
|
2018-02-20 09:30:36 +00:00
|
|
|
if (discoveryProcess.interrupted) return;
|
|
|
|
dispatch({
|
|
|
|
type: ADDRESS.SET_NONCE,
|
|
|
|
address: ethAddress,
|
|
|
|
nonce: nonce
|
|
|
|
});
|
|
|
|
|
|
|
|
const addressIsEmpty = nonce < 1 && !balance.greaterThan(0);
|
|
|
|
|
|
|
|
if (!addressIsEmpty) {
|
|
|
|
//dispatch( startDiscoveryProcess(device, discoveryProcess.coin) );
|
|
|
|
dispatch( discoverAddress(device, discoveryProcess) );
|
|
|
|
} else {
|
|
|
|
// release acquired sesssion
|
|
|
|
await TrezorConnect.getPublicKey({
|
|
|
|
device: {
|
|
|
|
path: device.path,
|
|
|
|
instance: device.instance,
|
|
|
|
state: device.checksum
|
|
|
|
},
|
|
|
|
path: "m/44'/60'/0'/0",
|
|
|
|
confirmation: false,
|
|
|
|
keepSession: false
|
|
|
|
});
|
|
|
|
if (discoveryProcess.interrupted) return;
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: DISCOVERY.COMPLETE,
|
|
|
|
device,
|
|
|
|
coin
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function startDiscoveryProcess(device: any, coin: string, ignoreCompleted?: boolean): any {
|
|
|
|
return (dispatch, getState) => {
|
|
|
|
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
if (!selected) {
|
|
|
|
// TODO: throw error
|
|
|
|
console.error("Start discovery: no selected device", device)
|
|
|
|
return;
|
|
|
|
} else if (selected.path !== device.path) {
|
|
|
|
console.error("Start discovery: requested device is not selected", device, selected)
|
|
|
|
return;
|
|
|
|
} else if (!selected.checksum) {
|
|
|
|
console.warn("Start discovery: Selected device wasn't authenticated yet...")
|
|
|
|
return;
|
|
|
|
}
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const discovery = getState().discovery;
|
|
|
|
let discoveryProcess: ?Discovery = discovery.find(d => d.checksum === device.checksum && d.coin === coin);
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
|
|
|
dispatch({
|
|
|
|
type: DISCOVERY.WAITING,
|
|
|
|
device,
|
|
|
|
coin
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!discoveryProcess) {
|
|
|
|
dispatch( beginDiscoveryProcess(device, coin) );
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (discoveryProcess.completed && !ignoreCompleted) {
|
|
|
|
dispatch({
|
|
|
|
type: DISCOVERY.COMPLETE,
|
|
|
|
device,
|
|
|
|
coin
|
|
|
|
});
|
|
|
|
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
|
|
|
|
// discovery cycle was interrupted
|
|
|
|
// start from beginning
|
|
|
|
dispatch( beginDiscoveryProcess(device, coin) );
|
|
|
|
} else {
|
|
|
|
dispatch( discoverAddress(device, discoveryProcess) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const restoreDiscovery = (): any => {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
|
|
|
|
if (selected && selected.connected && !selected.unacquired) {
|
|
|
|
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.checksum === selected.checksum && d.waitingForDevice);
|
|
|
|
if (discoveryProcess) {
|
|
|
|
dispatch( startDiscoveryProcess(selected, discoveryProcess.coin) );
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// there is no discovery process but it should be
|
|
|
|
// this is possible race condition when coin was changed in url but device wasn't authenticated yet
|
|
|
|
// try to discovery after CONNECT.AUTH_DEVICE action
|
|
|
|
export const checkDiscoveryStatus = (): any => {
|
|
|
|
return (dispatch, getState): void => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
if (!selected) return;
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
const urlParams = getState().router.location.params;
|
|
|
|
if (urlParams.coin) {
|
|
|
|
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.checksum === selected.checksum && d.coin === urlParams.coin);
|
|
|
|
if (!discoveryProcess) {
|
|
|
|
dispatch( startDiscoveryProcess(selected, urlParams.coin) );
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-13 11:01:37 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
export function stopDiscoveryProcess(device: any): any {
|
|
|
|
|
|
|
|
// TODO: release devices session
|
|
|
|
// corner case swtich /eth to /etc (discovery start stop - should not be async)
|
|
|
|
return {
|
|
|
|
type: DISCOVERY.STOP,
|
|
|
|
device
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
export function addAddress(): any {
|
|
|
|
return (dispatch, getState) => {
|
|
|
|
const selected = findSelectedDevice(getState().connect);
|
|
|
|
dispatch( startDiscoveryProcess(selected, getState().router.location.params.coin, true) ); // TODO: coin nicer
|
2017-12-13 11:01:37 +00:00
|
|
|
}
|
|
|
|
}
|