@ -1,7 +1,9 @@
/* @flow */
'use strict' ;
import TrezorConnect , { UI , DEVICE , DEVICE _EVENT , UI _EVENT , TRANSPORT _EVENT } from 'trezor-connect' ;
import TrezorConnect , {
UI , DEVICE , DEVICE _EVENT , UI _EVENT , TRANSPORT _EVENT ,
} from 'trezor-connect' ;
import * as TOKEN from './constants/token' ;
import * as CONNECT from './constants/TrezorConnect' ;
import * as NOTIFICATION from './constants/notification' ;
@ -20,17 +22,17 @@ import type {
TransportMessage ,
DeviceMessageType ,
TransportMessageType ,
UiMessageType
UiMessageType ,
} from 'trezor-connect' ;
import type {
import type {
Dispatch ,
GetState ,
Action ,
ThunkAction ,
AsyncAction ,
TrezorDevice ,
RouterLocationState
RouterLocationState ,
} from '~/flowtype' ;
@ -81,301 +83,276 @@ export type TrezorConnectAction = {
} ;
export const init = ( ) : AsyncAction => {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
// set listeners
TrezorConnect . on ( DEVICE _EVENT , ( event : DeviceMessage ) : void => {
// post event to reducers
const type : DeviceMessageType = event . type ; // assert flow type
dispatch ( {
type ,
device : event . payload
} ) ;
export const init = ( ) : AsyncAction => async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
// set listeners
TrezorConnect . on ( DEVICE _EVENT , ( event : DeviceMessage ) : void => {
// post event to reducers
const type : DeviceMessageType = event . type ; // assert flow type
dispatch ( {
type ,
device : event . payload ,
} ) ;
} ) ;
TrezorConnect . on ( UI _EVENT , ( event : UiMessage ) : void => {
// post event to reducers
const type : UiMessageType = event . type ; // assert flow type
dispatch ( {
type ,
payload : event . payload
} ) ;
TrezorConnect . on ( UI _EVENT , ( event : UiMessage ) : void => {
// post event to reducers
const type : UiMessageType = event . type ; // assert flow type
dispatch ( {
type ,
payload : event . payload ,
} ) ;
} ) ;
TrezorConnect . on ( TRANSPORT _EVENT , ( event : TransportMessage ) : void => {
// post event to reducers
const type : TransportMessageType = event . type ; // assert flow type
dispatch ( {
type ,
payload : event . payload
} ) ;
TrezorConnect . on ( TRANSPORT _EVENT , ( event : TransportMessage ) : void => {
// post event to reducers
const type : TransportMessageType = event . type ; // assert flow type
dispatch ( {
type ,
payload : event . payload ,
} ) ;
} ) ;
try {
await TrezorConnect . init ( {
transportReconnect : true ,
// connectSrc: 'https://localhost:8088/',
connectSrc : 'https://sisyfos.trezor.io/' ,
debug : false ,
popup : false ,
webusb : true ,
pendingTransportEvent : ( getState ( ) . devices . length < 1 )
} ) ;
} catch ( error ) {
// dispatch({
// type: CONNECT.INITIALIZATION_ERROR,
// error
// })
}
try {
await TrezorConnect . init ( {
transportReconnect : true ,
// connectSrc: 'https://localhost:8088/',
connectSrc : 'https://sisyfos.trezor.io/' ,
debug : false ,
popup : false ,
webusb : true ,
pendingTransportEvent : ( getState ( ) . devices . length < 1 ) ,
} ) ;
} catch ( error ) {
// dispatch({
// type: CONNECT.INITIALIZATION_ERROR,
// error
// })
}
}
} ;
// called after backend was initialized
// set listeners for connect/disconnect
export const postInit = ( ) : ThunkAction => {
return ( dispatch : Dispatch , getState : GetState ) : void => {
export const postInit = ( ) : ThunkAction => ( dispatch : Dispatch , getState : GetState ) : void => {
const handleDeviceConnect = ( device : Device ) => {
dispatch ( initConnectedDevice ( device ) ) ;
} ;
const handleDeviceConnect = ( device : Device ) => {
dispatch ( initConnectedDevice ( device ) ) ;
}
TrezorConnect . off ( DEVICE . CONNECT , handleDeviceConnect ) ;
TrezorConnect . off ( DEVICE . CONNECT _UNACQUIRED , handleDeviceConnect ) ;
TrezorConnect . off ( DEVICE . CONNECT , handleDeviceConnect ) ;
TrezorConnect . off ( DEVICE . CONNECT _UNACQUIRED , handleDeviceConnect ) ;
TrezorConnect . on ( DEVICE . CONNECT , handleDeviceConnect ) ;
TrezorConnect . on ( DEVICE . CONNECT _UNACQUIRED , handleDeviceConnect ) ;
TrezorConnect . on ( DEVICE . CONNECT , handleDeviceConnect ) ;
TrezorConnect . on ( DEVICE . CONNECT _UNACQUIRED , handleDeviceConnect ) ;
const { devices } = getState ( ) ;
const { device s } = getState ( ) ;
const { initialPathname , initialParam s } = getState ( ) . wallet ;
const { initialPathname , initialParams } = getState ( ) . wallet ;
if ( initialPathname ) {
dispatch ( {
type : WALLET . SET _INITIAL _URL ,
// pathname: null,
// params: null
} ) ;
}
if ( initialPathname ) {
dispatch ( {
type : WALLET . SET _INITIAL _URL ,
// pathname: null,
// params: null
} ) ;
}
if ( devices . length > 0 ) {
const unacquired : ? TrezorDevice = devices . find ( d => d . unacquired ) ;
if ( unacquired ) {
dispatch ( onSelectDevice ( unacquired ) ) ;
} else {
const latest : Array < TrezorDevice > = sortDevices ( devices ) ;
const firstConnected : ? TrezorDevice = latest . find ( d => d . connected ) ;
dispatch ( onSelectDevice ( firstConnected || latest [ 0 ] ) ) ;
// TODO
if ( initialParams ) {
if ( ! initialParams . hasOwnProperty ( 'network' ) && initialPathname !== getState ( ) . router . location . pathname ) {
// dispatch( push(initialPathname) );
} else {
if ( devices . length > 0 ) {
const unacquired : ? TrezorDevice = devices . find ( d => d . unacquired ) ;
if ( unacquired ) {
dispatch ( onSelectDevice ( unacquired ) ) ;
} else {
const latest : Array < TrezorDevice > = sortDevices ( devices ) ;
const firstConnected : ? TrezorDevice = latest . find ( d => d . connected ) ;
dispatch ( onSelectDevice ( firstConnected || latest [ 0 ] ) ) ;
// TODO
if ( initialParams ) {
if ( ! initialParams . hasOwnProperty ( "network" ) && initialPathname !== getState ( ) . router . location . pathname ) {
// dispatch( push(initialPathname) );
} else {
}
}
}
}
}
}
const sortDevices = ( devices : Array < TrezorDevice > ) : Array < TrezorDevice > => {
return devices . sort ( ( a , b ) => {
if ( ! a . ts || ! b . ts ) {
return - 1 ;
} else {
return a . ts > b . ts ? - 1 : 1 ;
}
} ) ;
}
} ;
export const initConnectedDevice = ( device : TrezorDevice | Device ) : ThunkAction => {
return ( dispatch : Dispatch , getState : GetState ) : void => {
const selected = getState ( ) . wallet . selectedDevice ;
// if (!selected || (selected && selected.state)) {
dispatch ( onSelectDevice ( device ) ) ;
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
const sortDevices = ( devices : Array < TrezorDevice > ) : Array < TrezorDevice > => devices . sort ( ( a , b ) => {
if ( ! a . ts || ! b . ts ) {
return - 1 ;
}
}
return a . ts > b . ts ? - 1 : 1 ;
} ) ;
export const initConnectedDevice = ( device : TrezorDevice | Device ) : ThunkAction => ( dispatch : Dispatch , getState : GetState ) : void => {
const selected = getState ( ) . wallet . selectedDevice ;
// if (!selected || (selected && selected.state)) {
dispatch ( onSelectDevice ( device ) ) ;
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
} ;
// selection from Aside dropdown button
// after device_connect event
// or after acquiring device
// device type could be local TrezorDevice or Device (from trezor-connect device_connect event)
export const onSelectDevice = ( device : TrezorDevice | Device ) : ThunkAction => {
return ( dispatch : Dispatch , getState : GetState ) : void => {
// || device.isUsedElsewhere
// switch to initial url and reset this value
if ( ! device . features ) {
dispatch ( push ( ` /device/ ${ device . path } /acquire ` ) ) ;
} else if ( device . features . bootloader _mode ) {
dispatch ( push ( ` /device/ ${ device . path } /bootloader ` ) ) ;
} else if ( ! device . features . initialized ) {
dispatch ( push ( ` /device/ ${ device . features . device _id } /initialize ` ) ) ;
} else if ( typeof device . instance === 'number' ) {
dispatch ( push ( ` /device/ ${ device . features . device _id } : ${ device . instance } ` ) ) ;
} else {
const deviceId : string = device . features . device _id ;
const urlParams : RouterLocationState = getState ( ) . router . location . state ;
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
let url : string = ` /device/ ${ deviceId } ` ;
let instance : ? number ;
// check if device is not TrezorDevice type
if ( ! device . hasOwnProperty ( 'ts' ) ) {
// its device from trezor-connect (called in initConnectedDevice triggered by device_connect event)
// need to lookup if there are unavailable instances
const available : Array < TrezorDevice > = getState ( ) . devices . filter ( d => d . path === device . path ) ;
const latest : Array < TrezorDevice > = sortDevices ( available ) ;
if ( latest . length > 0 && latest [ 0 ] . instance ) {
url += ` : ${ latest [ 0 ] . instance } ` ;
instance = latest [ 0 ] . instance ;
}
export const onSelectDevice = ( device : TrezorDevice | Device ) : ThunkAction => ( dispatch : Dispatch , getState : GetState ) : void => {
// || device.isUsedElsewhere
// switch to initial url and reset this value
if ( ! device . features ) {
dispatch ( push ( ` /device/ ${ device . path } /acquire ` ) ) ;
} else if ( device . features . bootloader _mode ) {
dispatch ( push ( ` /device/ ${ device . path } /bootloader ` ) ) ;
} else if ( ! device . features . initialized ) {
dispatch ( push ( ` /device/ ${ device . features . device _id } /initialize ` ) ) ;
} else if ( typeof device . instance === 'number' ) {
dispatch ( push ( ` /device/ ${ device . features . device _id } : ${ device . instance } ` ) ) ;
} else {
const deviceId : string = device . features . device _id ;
const urlParams : RouterLocationState = getState ( ) . router . location . state ;
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
let url : string = ` /device/ ${ deviceId } ` ;
let instance : ? number ;
// check if device is not TrezorDevice type
if ( ! device . hasOwnProperty ( 'ts' ) ) {
// its device from trezor-connect (called in initConnectedDevice triggered by device_connect event)
// need to lookup if there are unavailable instances
const available : Array < TrezorDevice > = getState ( ) . devices . filter ( d => d . path === device . path ) ;
const latest : Array < TrezorDevice > = sortDevices ( available ) ;
if ( latest . length > 0 && latest [ 0 ] . instance ) {
url += ` : ${ latest [ 0 ] . instance } ` ;
instance = latest [ 0 ] . instance ;
}
// check if current location is not set to this device
//dispatch( push(`/device/${ device.features.device_id }/network/etc/account/0`) );
}
// check if current location is not set to this device
//dispatch( push(`/device/${ device.features.device_id }/network/etc/account/0`) );
if ( urlParams . deviceInstance !== instance || urlParams . device !== deviceId ) {
dispatch ( push ( url ) ) ;
}
if ( urlParams . deviceInstance !== instance || urlParams . device !== deviceId ) {
dispatch ( push ( url ) ) ;
}
}
}
export const switchToFirstAvailableDevice = ( ) : AsyncAction => {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
} ;
const { devices } = getState ( ) ;
if ( devices . length > 0 ) {
// TODO: Priority:
// 1. First Unacquired
// 2. First connected
// 3. Saved with latest timestamp
const unacquired = devices . find ( d => d . unacquired ) ;
if ( unacquired ) {
dispatch ( initConnectedDevice ( unacquired ) ) ;
} else {
const latest : Array < TrezorDevice > = sortDevices ( devices ) ;
const firstConnected : ? TrezorDevice = latest . find ( d => d . connected ) ;
dispatch ( onSelectDevice ( firstConnected || latest [ 0 ] ) ) ;
}
export const switchToFirstAvailableDevice = ( ) : AsyncAction => async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
const { devices } = getState ( ) ;
if ( devices . length > 0 ) {
// TODO: Priority:
// 1. First Unacquired
// 2. First connected
// 3. Saved with latest timestamp
const unacquired = devices . find ( d => d . unacquired ) ;
if ( unacquired ) {
dispatch ( initConnectedDevice ( unacquired ) ) ;
} else {
dispatch ( push ( '/' ) ) ;
const latest : Array < TrezorDevice > = sortDevices ( devices ) ;
const firstConnected : ? TrezorDevice = latest . find ( d => d . connected ) ;
dispatch ( onSelectDevice ( firstConnected || latest [ 0 ] ) ) ;
}
} else {
dispatch ( push ( '/' ) ) ;
}
}
} ;
export const getSelectedDeviceState = ( ) : AsyncAction => {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
const selected = getState ( ) . wallet . selectedDevice ;
if ( selected
export const getSelectedDeviceState = ( ) : AsyncAction => async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
const selected = getState ( ) . wallet . selectedDevice ;
if ( selected
&& selected . connected
&& ( selected . features && ! selected . features . bootloader _mode && selected . features . initialized )
&& ! selected . state ) {
const response = await TrezorConnect . getDeviceState ( {
device : {
path : selected . path ,
instance : selected . instance ,
state : selected . state ,
} ,
useEmptyPassphrase : ! selected . instance ,
} ) ;
const response = await TrezorConnect . getDeviceState ( {
device : {
path : selected . path ,
instance : selected . instance ,
state : selected . state
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 } ,
} ) ;
dispatch ( getSelectedDeviceState ( ) ) ;
} ,
} ,
] ,
} ,
useEmptyPassphrase : ! selected . instance ,
} ) ;
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 }
} ) ;
dispatch ( getSelectedDeviceState ( ) ) ;
}
}
]
}
} ) ;
}
}
}
}
} ;
export const deviceDisconnect = ( device : Device ) : AsyncAction => {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
export const deviceDisconnect = ( device : Device ) : AsyncAction => async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
const selected : ? TrezorDevice = getState ( ) . wallet . selectedDevice ;
const selected : ? TrezorDevice = getState ( ) . wallet . selectedDevice ;
if ( device && device . features ) {
if ( selected && selected . features && selected . features . device _id === device . features . device _id ) {
dispatch ( DiscoveryActions . stop ( selected ) ) ;
}
if ( device && device . features ) {
if ( selected && selected . features && selected . features . device _id === device . features . device _id ) {
dispatch ( DiscoveryActions . stop ( selected ) ) ;
}
const instances = getState ( ) . devices . filter ( d => d . features && d . state && ! d . remember && d . features . device _id === device . features . device _id ) ;
if ( instances . length > 0 ) {
dispatch ( {
type : CONNECT . REMEMBER _REQUEST ,
device : instances [ 0 ] ,
instances ,
} ) ;
}
const instances = getState ( ) . devices . filter ( d => d . features && d . state && ! d . remember && d . features . device _id === device . features . device _id ) ;
if ( instances . length > 0 ) {
dispatch ( {
type : CONNECT . REMEMBER _REQUEST ,
device : instances [ 0 ] ,
instances ,
} ) ;
}
}
}
} ;
export const coinChanged = ( network : ? string ) : ThunkAction => {
return ( dispatch : Dispatch , getState : GetState ) : void => {
const selected : ? TrezorDevice = getState ( ) . wallet . selectedDevice ;
if ( ! selected ) return ;
export const coinChanged = ( network : ? string ) : ThunkAction => ( dispatch : Dispatch , getState : GetState ) : void => {
const selected : ? TrezorDevice = getState ( ) . wallet . selectedDevice ;
if ( ! selected ) return ;
dispatch ( DiscoveryActions . stop ( selected ) ) ;
dispatch ( DiscoveryActions . stop ( selected ) ) ;
if ( network ) {
dispatch ( DiscoveryActions . start ( selected , network ) ) ;
}
if ( network ) {
dispatch ( DiscoveryActions . start ( selected , network ) ) ;
}
}
} ;
export function reload ( ) : AsyncAction {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
}
} ;
}
export function acquire ( ) : AsyncAction {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
const selected : ? TrezorDevice = getState ( ) . wallet . selectedDevice ;
if ( ! selected ) return ;
dispatch ( {
type : CONNECT . START _ACQUIRING ,
} )
} ) ;
const response = await TrezorConnect . getFeatures ( {
const response = await TrezorConnect . getFeatures ( {
device : {
path : selected . path ,
} ,
@ -398,47 +375,41 @@ export function acquire(): AsyncAction {
// }
// }
// ]
}
} )
} ,
} ) ;
}
dispatch ( {
type : CONNECT . STOP _ACQUIRING ,
} )
}
} ) ;
} ;
}
export const gotoDeviceSettings = ( device : TrezorDevice ) : ThunkAction => {
return ( dispatch : Dispatch , getState : GetState ) : void => {
if ( device . features ) {
const devUrl : string = ` ${ device . features . device _id } ${ device . instance ? ` : ${ device . instance } ` : '' } ` ;
dispatch ( push ( ` /device/ ${ devUrl } /settings ` ) ) ;
}
export const gotoDeviceSettings = ( device : TrezorDevice ) : ThunkAction => ( dispatch : Dispatch , getState : GetState ) : void => {
if ( device . features ) {
const devUrl : string = ` ${ device . features . device _id } ${ device . instance ? ` : ${ device . instance } ` : '' } ` ;
dispatch ( push ( ` /device/ ${ devUrl } /settings ` ) ) ;
}
}
} ;
// called from Aside - device menu (forget single instance)
export const forget = ( device : TrezorDevice ) : Action => {
return {
type : CONNECT . FORGET _REQUEST ,
device
} ;
}
export const duplicateDevice = ( device : TrezorDevice ) : AsyncAction => {
return async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
dispatch ( {
type : CONNECT . TRY _TO _DUPLICATE ,
device
} )
}
}
export const forget = ( device : TrezorDevice ) : Action => ( {
type : CONNECT . FORGET _REQUEST ,
device ,
} ) ;
export const duplicateDevice = ( device : TrezorDevice ) : AsyncAction => async ( dispatch : Dispatch , getState : GetState ) : Promise < void > => {
dispatch ( {
type : CONNECT . TRY _TO _DUPLICATE ,
device ,
} ) ;
} ;
export function addAccount ( ) : ThunkAction {
return ( dispatch : Dispatch , getState : GetState ) : void => {
const selected = getState ( ) . wallet . selectedDevice ;
if ( ! selected ) return ;
dispatch ( DiscoveryActions . start ( selected , getState ( ) . router . location . state . network , true ) ) ; // TODO: network nicer
}
dispatch ( DiscoveryActions . start ( selected , getState ( ) . router . location . state . network , true ) ) ; // TODO: network nicer
} ;
}