import { WalletProvider } from '@liquality/wallet-provider' ; import { WalletError } from '@liquality/errors' ; import { caseInsensitiveEqual } from '@liquality/utils' ; import { Network , Address } from '@liquality/types' ; import HwTransport from '@ledgerhq/hw-transport' ; import { Debug } from '@liquality/debug' ; const debug = Debug ( 'ledger' ) ; interface IApp { transport : any ; } export type Newable < T > = { new ( ... args ) : T ; } ; export default abstract class LedgerProvider < TApp extends IApp > extends WalletProvider { _App : any ; _network : Network ; _ledgerScrambleKey : string ; _transport : HwTransport ; _Transport : any ; _appInstance : TApp ; constructor ( options ) { super ( { network : options . network } ) ; this . _App = options . App ; this . _Transport = options . Transport ; this . _network = options . network ; this . _ledgerScrambleKey = options . ledgerScrambleKey ; } async createTransport ( ) { if ( ! this . _transport ) { debug ( 'creating ledger transport' ) ; this . _transport = await this . _Transport . create ( ) ; debug ( 'ledger transport created' ) ; this . _transport . on ( 'disconnect' , ( ) => { debug ( 'ledger disconnected' ) ; this . _appInstance = null ; this . _transport = null ; } ) ; } } errorProxy ( target , func ) { const method = target [ func ] ; if ( Object . getOwnPropertyNames ( target ) . includes ( func ) && typeof method === 'function' ) { return async ( ... args ) => { debug ( `calling "${ func }" on ledger object` , args ) ; try { const result = await method . bind ( target ) ( ... args ) ; debug ( `result from "${ func }" on ledger object` , result ) ; return result ; } catch ( e ) { const { name , ... errorNoName } = e ; this . _transport = null ; this . _appInstance = null ; throw new WalletError ( e . toString ( ) , errorNoName ) ; } } ; } else { return method ; } } async getApp ( ) { try { await this . createTransport ( ) ; } catch ( e ) { const { name , ... errorNoName } = e ; throw new WalletError ( e . toString ( ) , errorNoName ) ; } if ( ! this . _appInstance ) { this . _appInstance = new Proxy ( new this . _App ( this . _transport ) , { get : this . errorProxy . bind ( this ) } ) ; } return this . _appInstance ; } async isWalletAvailable ( ) { const app = await this . getApp ( ) ; if ( ! app . transport . scrambleKey ) { app . transport . setScrambleKey ( this . _ledgerScrambleKey ) ; } const exchangeTimeout = app . transport . exchangeTimeout ; app . transport . setExchangeTimeout ( 2000 ) ; try { await this . _transport . send ( 0xe0 , 0xc0 , 0x00 , 0x00 ) ; } catch ( e ) { return false ; } finally { app . transport . setExchangeTimeout ( exchangeTimeout ) ; } return true ; } async getConnectedNetwork ( ) { return this . _network ; } async getWalletAddress ( address ) { let index = 0 ; let change = false ; const maxAddresses = 5000 ; const addressesPerCall = 50 ; while ( index < maxAddresses ) { const addrs = await this . getAddresses ( index , addressesPerCall , change ) ; const addr = addrs . find ( ( addr ) => caseInsensitiveEqual ( addr . address , address ) ) ; if ( addr ) return addr ; index += addressesPerCall ; if ( index === maxAddresses && change === false ) { index = 0 ; change = true ; } } throw new Error ( 'Ledger: Wallet does not contain address' ) ; } }