@@ -7,12 +7,17 @@ import {
7
7
import ledgerIcon from '../wallet-icons/icon-ledger'
8
8
9
9
import createProvider from './providerEngine'
10
+ import { generateAddresses , isValidPath } from './hd-wallet'
10
11
11
12
import TransportU2F from '@ledgerhq/hw-transport-u2f'
12
13
import Eth from '@ledgerhq/hw-app-eth'
13
14
import * as EthereumTx from 'ethereumjs-tx'
14
15
15
16
import buffer from 'buffer'
17
+ import { getAddress } from '../../../utilities'
18
+
19
+ const LEDGER_LIVE_PATH = `m/44'/60'`
20
+ const ACCOUNTS_TO_GET = 5
16
21
17
22
function ledger ( options : LedgerOptions & CommonWalletOptions ) : WalletModule {
18
23
const { rpcUrl, networkId, preferred, label, iconSrc, svg } = options
@@ -36,7 +41,7 @@ function ledger(options: LedgerOptions & CommonWalletOptions): WalletModule {
36
41
interface : {
37
42
name : 'Ledger' ,
38
43
connect : provider . enable ,
39
- disconnect : ( ) => provider . stop ( ) ,
44
+ disconnect : provider . disconnect ,
40
45
address : {
41
46
get : async ( ) => provider . getPrimaryAddress ( )
42
47
} ,
@@ -65,10 +70,15 @@ async function ledgerProvider(options: {
65
70
networkName : ( id : number ) => string
66
71
} ) {
67
72
const { networkId, rpcUrl, BigNumber, networkName } = options
68
- const basePath = networkIdToDerivationPath ( networkId )
69
73
74
+ let dPath = ''
70
75
let addressToPath = new Map ( )
71
76
let enabled : boolean = false
77
+ let customPath = false
78
+
79
+ let account :
80
+ | undefined
81
+ | { publicKey : string ; chainCode : string ; path : string }
72
82
73
83
const provider = createProvider ( {
74
84
getAccounts : ( callback : any ) => {
@@ -84,37 +94,66 @@ async function ledgerProvider(options: {
84
94
rpcUrl
85
95
} )
86
96
87
- provider . getPrimaryAddress = getPrimaryAddress
88
- provider . getAllAccountsAndBalances = getAllAccountsAndBalances
97
+ provider . setPath = setPath
98
+ provider . dPath = dPath
89
99
provider . enable = enable
90
100
provider . setPrimaryAccount = setPrimaryAccount
101
+ provider . getPrimaryAddress = getPrimaryAddress
102
+ provider . getAccounts = getAccounts
103
+ provider . getMoreAccounts = getMoreAccounts
91
104
provider . getBalance = getBalance
105
+ provider . getBalances = getBalances
92
106
provider . send = provider . sendAsync
107
+ provider . disconnect = disconnect
108
+ provider . isCustomPath = isCustomPath
93
109
94
- function enable ( ) {
95
- enabled = true
96
- return getAccounts ( 1 )
110
+ let transport
111
+ let eth : any
112
+
113
+ try {
114
+ transport = await TransportU2F . create ( )
115
+ eth = new Eth ( transport )
116
+ } catch ( error ) {
117
+ throw new Error ( 'Error connecting to Ledger wallet' )
97
118
}
98
119
99
- function addresses ( ) {
100
- return Array . from ( addressToPath . keys ( ) )
120
+ function disconnect ( ) {
121
+ console . log ( 'resetting' )
122
+ dPath = ''
123
+ addressToPath = new Map ( )
124
+ enabled = false
125
+ provider . stop ( )
101
126
}
102
127
103
- function getPrimaryAddress ( ) {
104
- return enabled ? addresses ( ) [ 0 ] : undefined
128
+ async function setPath ( path : string , custom ?: boolean ) {
129
+ if ( custom ) {
130
+ if ( ! isValidPath ( path ) ) {
131
+ return false
132
+ }
133
+
134
+ const address = await getAddress ( path )
135
+ addressToPath . set ( address , path )
136
+ customPath = true
137
+ return true
138
+ }
139
+
140
+ customPath = false
141
+ dPath = path
142
+
143
+ return true
105
144
}
106
145
107
- async function getAllAccountsAndBalances ( amountToGet : number = 5 ) {
108
- const accounts = await getAccounts ( amountToGet , true )
109
- return Promise . all (
110
- accounts . map (
111
- ( address : string ) =>
112
- new Promise ( async resolve => {
113
- const balance = await getBalance ( address )
114
- resolve ( { address , balance } )
115
- } )
116
- )
117
- )
146
+ function isCustomPath ( ) {
147
+ return customPath
148
+ }
149
+
150
+ function enable ( ) {
151
+ enabled = true
152
+ return getAccounts ( )
153
+ }
154
+
155
+ function addresses ( ) {
156
+ return Array . from ( addressToPath . keys ( ) )
118
157
}
119
158
120
159
function setPrimaryAccount ( address : string ) {
@@ -129,65 +168,98 @@ async function ledgerProvider(options: {
129
168
addressToPath = new Map ( accounts )
130
169
}
131
170
132
- function getAccounts (
133
- numberToGet : number = 1 ,
134
- getMore ?: boolean
135
- ) : Promise < any [ ] > {
136
- return new Promise ( async ( resolve , reject ) => {
137
- if ( ! enabled ) {
138
- resolve ( [ null ] )
139
- }
171
+ async function getAddress ( path : string ) {
172
+ try {
173
+ const result = await eth . getAddress ( path )
174
+ return result . address
175
+ } catch ( error ) { }
176
+ }
140
177
141
- const addressesAlreadyFetched = addressToPath . size
178
+ async function getPublicKey ( eth : any ) {
179
+ if ( ! dPath ) {
180
+ throw new Error ( 'a derivation path is needed to get the public key' )
181
+ }
182
+
183
+ try {
184
+ const result = await eth . getAddress ( dPath , false , true )
185
+ const { publicKey, chainCode } = result
142
186
143
- if ( addressesAlreadyFetched > 0 && ! getMore ) {
144
- return resolve ( addresses ( ) )
187
+ account = {
188
+ publicKey,
189
+ chainCode,
190
+ path : dPath
145
191
}
146
192
147
- const paths = [ ]
193
+ return account
194
+ } catch ( error ) {
195
+ throw new Error (
196
+ 'There was a problem getting access to your Ledger accounts.'
197
+ )
198
+ }
199
+ }
148
200
149
- if ( numberToGet > 1 ) {
150
- for (
151
- let i = addressesAlreadyFetched ;
152
- i < numberToGet + addressesAlreadyFetched ;
153
- i ++
154
- ) {
155
- const ledgerLive = `${ basePath } /${ i } '/0/0`
156
- const legacy = `${ basePath } /0'/${ i } '`
201
+ function getPrimaryAddress ( ) {
202
+ return enabled ? addresses ( ) [ 0 ] : undefined
203
+ }
157
204
158
- paths . push ( ledgerLive , legacy )
159
- }
160
- } else {
161
- paths . push ( `${ basePath } /0'/0` )
162
- }
205
+ async function getMoreAccounts ( ) {
206
+ const accounts = await getAccounts ( true )
207
+ return accounts && getBalances ( accounts )
208
+ }
209
+
210
+ async function getAccounts ( getMore ?: boolean ) {
211
+ if ( ! enabled ) {
212
+ return [ undefined ]
213
+ }
163
214
164
- let transport
165
- let eth
215
+ if ( addressToPath . size > 0 && ! getMore ) {
216
+ return addresses ( )
217
+ }
166
218
167
- try {
168
- transport = await TransportU2F . create ( )
169
- eth = new Eth ( transport )
170
- } catch ( error ) {
171
- reject ( { message : 'Error connecting to Ledger wallet' } )
219
+ if ( dPath === LEDGER_LIVE_PATH ) {
220
+ const currentAccounts = addressToPath . size
221
+ const paths = [ ]
222
+ for (
223
+ let i = currentAccounts ;
224
+ i < ACCOUNTS_TO_GET + currentAccounts ;
225
+ i ++
226
+ ) {
227
+ paths . push ( `${ LEDGER_LIVE_PATH } /${ i } '/0/0` )
172
228
}
173
229
174
230
for ( const path of paths ) {
175
231
try {
176
- const { address } = await eth . getAddress ( path )
177
- addressToPath . set ( address . toLowerCase ( ) , path )
178
- } catch ( err ) {
179
- return reject ( {
180
- message : 'There was a problem trying to connect to your Ledger.'
181
- } )
232
+ const res = await eth . getAddress ( path )
233
+ addressToPath . set ( res . address , path )
234
+ } catch ( error ) {
235
+ console . log ( { error } )
182
236
}
183
237
}
238
+ } else {
239
+ const accountInfo = account || ( await getPublicKey ( eth ) )
184
240
185
- const allAddresses = addresses ( )
241
+ if ( ! accountInfo ) return [ undefined ]
186
242
187
- transport . close ( )
243
+ const addressInfo = generateAddresses ( accountInfo , addressToPath . size )
188
244
189
- resolve ( allAddresses )
190
- } )
245
+ addressInfo . forEach ( ( { dPath, address } ) => {
246
+ addressToPath . set ( address , dPath )
247
+ } )
248
+ }
249
+
250
+ return addresses ( )
251
+ }
252
+
253
+ function getBalances ( addresses : Array < string > ) {
254
+ return Promise . all (
255
+ addresses . map (
256
+ address =>
257
+ new Promise ( async resolve => {
258
+ const balance = await getBalance ( address )
259
+ resolve ( { address, balance } )
260
+ } )
261
+ )
262
+ )
191
263
}
192
264
193
265
function getBalance ( address : string ) {
0 commit comments