edge-currency-accountbased 0.7.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +713 -0
  2. package/LICENSE +29 -0
  3. package/README.md +63 -0
  4. package/index.js +3 -0
  5. package/lib/binance/bnbEngine.js +591 -0
  6. package/lib/binance/bnbInfo.js +43 -0
  7. package/lib/binance/bnbPlugin.js +168 -0
  8. package/lib/binance/bnbSchema.js +83 -0
  9. package/lib/binance/bnbTypes.js +39 -0
  10. package/lib/common/engine.js +918 -0
  11. package/lib/common/plugin.js +152 -0
  12. package/lib/common/schema.js +108 -0
  13. package/lib/common/types.js +85 -0
  14. package/lib/common/utils.js +378 -0
  15. package/lib/eos/eosEngine.js +1216 -0
  16. package/lib/eos/eosInfo.js +98 -0
  17. package/lib/eos/eosPlugin.js +314 -0
  18. package/lib/eos/eosSchema.js +190 -0
  19. package/lib/eos/eosTypes.js +88 -0
  20. package/lib/eos/telosInfo.js +94 -0
  21. package/lib/eos/waxInfo.js +95 -0
  22. package/lib/ethereum/etcInfo.js +121 -0
  23. package/lib/ethereum/ethEngine.js +832 -0
  24. package/lib/ethereum/ethInfo.js +1300 -0
  25. package/lib/ethereum/ethMiningFees.js +157 -0
  26. package/lib/ethereum/ethNetwork.js +2195 -0
  27. package/lib/ethereum/ethPlugin.js +377 -0
  28. package/lib/ethereum/ethSchema.js +61 -0
  29. package/lib/ethereum/ethTypes.js +461 -0
  30. package/lib/ethereum/ftminfo.js +102 -0
  31. package/lib/ethereum/rskInfo.js +101 -0
  32. package/lib/fio/fioConst.js +38 -0
  33. package/lib/fio/fioEngine.js +1250 -0
  34. package/lib/fio/fioError.js +38 -0
  35. package/lib/fio/fioInfo.js +72 -0
  36. package/lib/fio/fioPlugin.js +486 -0
  37. package/lib/fio/fioSchema.js +56 -0
  38. package/lib/index.js +44 -0
  39. package/lib/pluginError.js +32 -0
  40. package/lib/react-native/edge-currency-accountbased.js +239635 -0
  41. package/lib/react-native/edge-currency-accountbased.js.map +1 -0
  42. package/lib/react-native-io.js +41 -0
  43. package/lib/stellar/stellarEngine.js +563 -0
  44. package/lib/stellar/stellarInfo.js +37 -0
  45. package/lib/stellar/stellarPlugin.js +215 -0
  46. package/lib/stellar/stellarSchema.js +54 -0
  47. package/lib/stellar/stellarTypes.js +66 -0
  48. package/lib/tezos/tezosEngine.js +497 -0
  49. package/lib/tezos/tezosInfo.js +60 -0
  50. package/lib/tezos/tezosPlugin.js +174 -0
  51. package/lib/tezos/tezosTypes.js +110 -0
  52. package/lib/xrp/xrpEngine.js +583 -0
  53. package/lib/xrp/xrpInfo.js +47 -0
  54. package/lib/xrp/xrpPlugin.js +229 -0
  55. package/lib/xrp/xrpSchema.js +74 -0
  56. package/lib/xrp/xrpTypes.js +38 -0
  57. package/package.json +139 -0
  58. package/postinstall.sh +7 -0
@@ -0,0 +1,918 @@
1
+ //
2
+
3
+ import { bns } from 'biggystring'
4
+
5
+ import {
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+ InsufficientFundsError,
22
+ SpendToSelfError
23
+ } from 'edge-core-js/types'
24
+
25
+ import { CurrencyPlugin } from './plugin.js'
26
+ import {
27
+ asCurrencyCodeOptions,
28
+ checkCustomToken,
29
+ checkEdgeSpendInfo
30
+ } from './schema.js'
31
+ import {
32
+
33
+ DATA_STORE_FILE,
34
+ TRANSACTION_STORE_FILE,
35
+ TXID_LIST_FILE,
36
+ TXID_MAP_FILE,
37
+ WalletLocalData
38
+ } from './types.js'
39
+ import { cleanTxLogs, getDenomInfo, normalizeAddress } from './utils.js'
40
+
41
+ const SAVE_DATASTORE_MILLISECONDS = 10000
42
+ const MAX_TRANSACTIONS = 1000
43
+ const DROPPED_TX_TIME_GAP = 3600 * 24 // 1 Day
44
+
45
+ export class CurrencyEngine {
46
+
47
+
48
+
49
+
50
+
51
+
52
+ // Each currency code can be a 0-1 value
53
+ // Each currency code can be a 0-1 value
54
+
55
+
56
+
57
+
58
+
59
+ // Maps txid to index of tx in
60
+ // Map of array of txids in chronological order
61
+ // Transactions that have changed and need to be added
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+ constructor(
73
+ currencyPlugin,
74
+ walletInfo,
75
+ opts
76
+ ) {
77
+ const currencyCode = currencyPlugin.currencyInfo.currencyCode
78
+ const { walletLocalDisklet, callbacks } = opts
79
+
80
+ this.currencyPlugin = currencyPlugin
81
+ this.io = currencyPlugin.io
82
+ this.log = opts.log
83
+ this.engineOn = false
84
+ this.addressesChecked = false
85
+ this.tokenCheckBalanceStatus = {}
86
+ this.tokenCheckTransactionsStatus = {}
87
+ this.walletLocalDataDirty = false
88
+ this.transactionsChangedArray = []
89
+ this.transactionList = {}
90
+ this.transactionListDirty = false
91
+ this.transactionsLoaded = false
92
+ this.txIdMap = {}
93
+ this.txIdList = {}
94
+ this.walletInfo = walletInfo
95
+ this.walletId = walletInfo.id ? `${walletInfo.id} - ` : ''
96
+ this.currencyInfo = currencyPlugin.currencyInfo
97
+ this.allTokens = currencyPlugin.currencyInfo.metaTokens.slice(0)
98
+ this.customTokens = []
99
+ this.timers = {}
100
+
101
+ this.transactionList[currencyCode] = []
102
+ this.txIdMap[currencyCode] = {}
103
+ this.txIdList[currencyCode] = []
104
+
105
+ if (opts.userSettings != null) {
106
+ this.currentSettings = opts.userSettings
107
+ } else {
108
+ this.currentSettings = this.currencyInfo.defaultSettings
109
+ }
110
+
111
+ this.currencyEngineCallbacks = callbacks
112
+ this.walletLocalDisklet = walletLocalDisklet
113
+
114
+ if (typeof this.walletInfo.keys.publicKey !== 'string') {
115
+ this.walletInfo.keys.publicKey = walletInfo.keys.publicKey
116
+ }
117
+ this.log(
118
+ `Created Wallet Type ${this.walletInfo.type} for Currency Plugin ${this.currencyInfo.pluginId}`
119
+ )
120
+ }
121
+
122
+ isSpendTx(edgeTransaction) {
123
+ if (edgeTransaction.nativeAmount) {
124
+ if (edgeTransaction.nativeAmount.slice(0, 1) === '-') {
125
+ return true
126
+ }
127
+ if (bns.gt(edgeTransaction.nativeAmount, '0')) {
128
+ return false
129
+ }
130
+ }
131
+ let out = true
132
+ if (
133
+ edgeTransaction.ourReceiveAddresses &&
134
+ edgeTransaction.ourReceiveAddresses.length
135
+ ) {
136
+ for (const addr of edgeTransaction.ourReceiveAddresses) {
137
+ if (addr === this.walletLocalData.publicKey) {
138
+ out = false
139
+ }
140
+ }
141
+ }
142
+ return out
143
+ }
144
+
145
+ async loadTransactions() {
146
+ if (this.transactionsLoaded) {
147
+ this.log('Transactions already loaded')
148
+ return
149
+ }
150
+ this.transactionsLoaded = true
151
+
152
+ const disklet = this.walletLocalDisklet
153
+ let txIdList
154
+ let txIdMap
155
+ let transactionList
156
+ try {
157
+ const result = await disklet.getText(TXID_LIST_FILE)
158
+ txIdList = JSON.parse(result)
159
+ } catch (e) {
160
+ this.log('Could not load txidList file. Failure is ok on new device')
161
+ await disklet.setText(TXID_LIST_FILE, JSON.stringify(this.txIdList))
162
+ }
163
+ try {
164
+ const result = await disklet.getText(TXID_MAP_FILE)
165
+ txIdMap = JSON.parse(result)
166
+ } catch (e) {
167
+ this.log('Could not load txidMap file. Failure is ok on new device')
168
+ await disklet.setText(TXID_MAP_FILE, JSON.stringify(this.txIdMap))
169
+ }
170
+
171
+ try {
172
+ const result = await disklet.getText(TRANSACTION_STORE_FILE)
173
+ transactionList = JSON.parse(result)
174
+ } catch (e) {
175
+ if (e.code === 'ENOENT') {
176
+ this.log(
177
+ 'Could not load transactionList file. Failure is ok on new device'
178
+ )
179
+ await disklet.setText(
180
+ TRANSACTION_STORE_FILE,
181
+ JSON.stringify(this.transactionList)
182
+ )
183
+ } else {
184
+ this.log.crash(e, this.walletLocalData)
185
+ }
186
+ }
187
+
188
+ let isEmptyTransactions = true
189
+ for (const cc of Object.keys(this.transactionList)) {
190
+ if (this.transactionList[cc] && this.transactionList[cc].length > 0) {
191
+ isEmptyTransactions = false
192
+ break
193
+ }
194
+ }
195
+
196
+ for (const cc of Object.keys(this.transactionList)) {
197
+ if (
198
+ this.transactionList[cc] !== undefined &&
199
+ this.transactionList[cc].length > 0
200
+ ) {
201
+ if (
202
+ transactionList !== undefined &&
203
+ transactionList[cc] !== undefined &&
204
+ transactionList[cc].length < this.transactionList[cc].length
205
+ ) {
206
+ this.log.crash(
207
+ new Error(
208
+ `Transaction list length mismatch for ${cc}: on disk ${transactionList[cc].length} txs < in memory ${this.transactionList[cc].length} txs`
209
+ ),
210
+ {
211
+ ...transactionList,
212
+ ...this.transactionList,
213
+ ...this.walletLocalData
214
+ }
215
+ )
216
+ }
217
+ }
218
+ }
219
+
220
+ if (isEmptyTransactions) {
221
+ // Easy, just copy everything over
222
+ this.transactionList = transactionList || this.transactionList
223
+ this.txIdList = txIdList || this.txIdList
224
+ this.txIdMap = txIdMap || this.txIdMap
225
+ } else if (transactionList != null) {
226
+ // Manually add transactions via addTransaction()
227
+ for (const cc of Object.keys(transactionList)) {
228
+ for (const edgeTransaction of transactionList[cc]) {
229
+ this.addTransaction(cc, edgeTransaction)
230
+ }
231
+ }
232
+ }
233
+ for (const currencyCode in this.transactionList) {
234
+ this.walletLocalData.numTransactions[currencyCode] =
235
+ this.transactionList[currencyCode].length
236
+ }
237
+ }
238
+
239
+ async loadEngine(
240
+ plugin,
241
+ walletInfo,
242
+ opts
243
+ ) {
244
+ if (!this.walletInfo.keys.publicKey) {
245
+ const pubKeys = await this.currencyPlugin.derivePublicKey(this.walletInfo)
246
+ this.walletInfo.keys.publicKey = pubKeys.publicKey
247
+ }
248
+
249
+ const disklet = this.walletLocalDisklet
250
+ try {
251
+ const result = await disklet.getText(DATA_STORE_FILE)
252
+ this.walletLocalData = new WalletLocalData(
253
+ result,
254
+ this.currencyInfo.currencyCode
255
+ )
256
+ this.walletLocalData.publicKey = this.walletInfo.keys.publicKey
257
+ } catch (err) {
258
+ try {
259
+ this.log('No walletLocalData setup yet: Failure is ok')
260
+ this.walletLocalData = new WalletLocalData(
261
+ null,
262
+ this.currencyInfo.currencyCode
263
+ )
264
+ this.walletLocalData.publicKey = this.walletInfo.keys.publicKey
265
+ await disklet.setText(
266
+ DATA_STORE_FILE,
267
+ JSON.stringify(this.walletLocalData)
268
+ )
269
+ } catch (e) {
270
+ this.log.error(
271
+ 'Error writing to localDataStore. Engine not started:' + err
272
+ )
273
+ throw e
274
+ }
275
+ }
276
+
277
+ for (const token of this.walletLocalData.enabledTokens) {
278
+ this.tokenCheckBalanceStatus[token] = 0
279
+ this.tokenCheckTransactionsStatus[token] = 0
280
+ }
281
+
282
+ // Initialize walletLocalData.lastTransactionQueryHeight for
283
+ // backwards-compatibility
284
+ if (!this.walletLocalData.lastTransactionQueryHeight) {
285
+ for (const token of this.walletLocalData.enabledTokens) {
286
+ this.walletLocalData.lastTransactionQueryHeight[token] =
287
+ this.walletLocalData.lastAddressQueryHeight || 0
288
+ }
289
+ }
290
+ this.doInitialBalanceCallback()
291
+ }
292
+
293
+ findTransaction(currencyCode, txid) {
294
+ if (this.txIdMap[currencyCode]) {
295
+ const index = this.txIdMap[currencyCode][txid]
296
+ if (typeof index === 'number') {
297
+ return index
298
+ }
299
+ }
300
+ return -1
301
+ }
302
+
303
+ sortTxByDate(a, b) {
304
+ return b.date - a.date
305
+ }
306
+
307
+ // Add or update tx in transactionList
308
+ addTransaction(
309
+ currencyCode,
310
+ edgeTransaction,
311
+ lastSeenTime
312
+ ) {
313
+ this.log('executing addTransaction: ', edgeTransaction.txid)
314
+ // set otherParams if not already set
315
+ if (!edgeTransaction.otherParams) {
316
+ edgeTransaction.otherParams = {}
317
+ }
318
+
319
+ if (edgeTransaction.blockHeight < 1) {
320
+ edgeTransaction.otherParams.lastSeenTime =
321
+ lastSeenTime || Math.round(Date.now() / 1000)
322
+ }
323
+ const txid = normalizeAddress(edgeTransaction.txid)
324
+ const idx = this.findTransaction(currencyCode, txid)
325
+ // if blockHeight of transaction is higher than known blockHeight
326
+ // then set transaction's blockHeight as the highest known blockHeight
327
+ if (edgeTransaction.blockHeight > this.currencyPlugin.highestTxHeight) {
328
+ this.currencyPlugin.highestTxHeight = edgeTransaction.blockHeight
329
+ }
330
+
331
+ let needsReSort = false
332
+ // if transaction doesn't exist in database
333
+ if (idx === -1) {
334
+ if (
335
+ // if unconfirmed spend then increment # uncofirmed spend TX's
336
+ this.isSpendTx(edgeTransaction) &&
337
+ edgeTransaction.blockHeight === 0
338
+ ) {
339
+ this.walletLocalData.numUnconfirmedSpendTxs++
340
+ this.walletLocalDataDirty = true
341
+ }
342
+
343
+ needsReSort = true
344
+ // if currency's transactionList is uninitialized then initialize
345
+ if (typeof this.transactionList[currencyCode] === 'undefined') {
346
+ this.transactionList[currencyCode] = []
347
+ } else if (
348
+ this.transactionList[currencyCode].length >= MAX_TRANSACTIONS
349
+ ) {
350
+ return
351
+ }
352
+ // add transaction to list of tx's, and array of changed transactions
353
+ this.transactionList[currencyCode].push(edgeTransaction)
354
+ this.walletLocalData.numTransactions[currencyCode] =
355
+ this.transactionList[currencyCode].length
356
+ this.walletLocalDataDirty = true
357
+
358
+ this.transactionListDirty = true
359
+ this.transactionsChangedArray.push(edgeTransaction)
360
+ this.log.warn('addTransaction new tx: ', edgeTransaction.txid)
361
+ } else {
362
+ // Already have this tx in the database. See if anything changed
363
+ const transactionsArray = this.transactionList[currencyCode]
364
+ const edgeTx = transactionsArray[idx]
365
+
366
+ const { otherParams: otherParamsOld = {} } = edgeTx
367
+ const { otherParams: otherParamsNew = {} } = edgeTransaction
368
+ if (
369
+ // if something in the transaction has changed?
370
+ edgeTx.blockHeight < edgeTransaction.blockHeight ||
371
+ (edgeTx.blockHeight === 0 && edgeTransaction.blockHeight < 0) ||
372
+ (edgeTx.blockHeight === edgeTransaction.blockHeight &&
373
+ (edgeTx.networkFee !== edgeTransaction.networkFee ||
374
+ edgeTx.nativeAmount !== edgeTransaction.nativeAmount ||
375
+ otherParamsOld.lastSeenTime !== otherParamsNew.lastSeenTime ||
376
+ edgeTx.date !== edgeTransaction.date))
377
+ ) {
378
+ // If a spend transaction goes from unconfirmed to dropped or confirmed,
379
+ // decrement numUnconfirmedSpendTxs
380
+ if (
381
+ this.isSpendTx(edgeTransaction) &&
382
+ edgeTransaction.blockHeight !== 0 &&
383
+ edgeTx.blockHeight === 0
384
+ ) {
385
+ this.walletLocalData.numUnconfirmedSpendTxs--
386
+ }
387
+ if (edgeTx.date !== edgeTransaction.date) {
388
+ needsReSort = true
389
+ }
390
+ this.log.warn(
391
+ `addTransaction: update ${edgeTransaction.txid} height:${edgeTransaction.blockHeight}`
392
+ )
393
+ this.walletLocalDataDirty = true
394
+ this.updateTransaction(currencyCode, edgeTransaction, idx)
395
+ } else {
396
+ // this.log(sprintf('Old transaction. No Update: %s', tx.hash))
397
+ }
398
+ }
399
+ if (needsReSort) {
400
+ this.sortTransactions(currencyCode)
401
+ }
402
+ }
403
+
404
+ sortTransactions(currencyCode) {
405
+ // Sort
406
+ this.transactionList[currencyCode].sort(this.sortTxByDate)
407
+ // Add to txidMap
408
+ const txIdList = []
409
+ let i = 0
410
+ for (const tx of this.transactionList[currencyCode]) {
411
+ if (!this.txIdMap[currencyCode]) {
412
+ this.txIdMap[currencyCode] = {}
413
+ }
414
+ this.txIdMap[currencyCode][normalizeAddress(tx.txid)] = i
415
+ txIdList.push(normalizeAddress(tx.txid))
416
+ i++
417
+ }
418
+ this.txIdList[currencyCode] = txIdList
419
+ }
420
+
421
+ checkDroppedTransactionsThrottled() {
422
+ const now = Date.now() / 1000
423
+ if (
424
+ now - this.walletLocalData.lastCheckedTxsDropped >
425
+ DROPPED_TX_TIME_GAP
426
+ ) {
427
+ this.checkDroppedTransactions(now)
428
+ this.walletLocalData.lastCheckedTxsDropped = now
429
+ this.walletLocalDataDirty = true
430
+ if (this.transactionsChangedArray.length > 0) {
431
+ this.currencyEngineCallbacks.onTransactionsChanged(
432
+ this.transactionsChangedArray
433
+ )
434
+ this.transactionsChangedArray = []
435
+ }
436
+ }
437
+ }
438
+
439
+ checkDroppedTransactions(dateNow) {
440
+ let numUnconfirmedSpendTxs = 0
441
+ for (const currencyCode in this.transactionList) {
442
+ // const droppedTxIndices: Array<number> = []
443
+ for (let i = 0; i < this.transactionList[currencyCode].length; i++) {
444
+ const tx = this.transactionList[currencyCode][i]
445
+ if (tx.blockHeight === 0) {
446
+ const { otherParams = {} } = tx
447
+ const lastSeen = otherParams.lastSeenTime
448
+ if (dateNow - lastSeen > DROPPED_TX_TIME_GAP) {
449
+ // droppedTxIndices.push(i)
450
+ tx.blockHeight = -1
451
+ tx.nativeAmount = '0'
452
+ this.transactionsChangedArray.push(tx)
453
+ // delete this.txIdMap[currencyCode][tx.txid]
454
+ } else if (this.isSpendTx(tx)) {
455
+ // Still have a pending spend transaction in the tx list
456
+ numUnconfirmedSpendTxs++
457
+ }
458
+ }
459
+ }
460
+ // Delete transactions in reverse order
461
+ // for (let i = droppedTxIndices.length - 1; i >= 0; i--) {
462
+ // const droppedIndex = droppedTxIndices[i]
463
+ // this.transactionList[currencyCode].splice(droppedIndex, 1)
464
+ // }
465
+ // if (droppedTxIndices.length) {
466
+ // this.sortTransactions(currencyCode)
467
+ // }
468
+ }
469
+ this.walletLocalData.numUnconfirmedSpendTxs = numUnconfirmedSpendTxs
470
+ this.walletLocalDataDirty = true
471
+ }
472
+
473
+ updateTransaction(
474
+ currencyCode,
475
+ edgeTransaction,
476
+ idx
477
+ ) {
478
+ // Update the transaction
479
+ this.transactionList[currencyCode][idx] = edgeTransaction
480
+ this.transactionListDirty = true
481
+ this.transactionsChangedArray.push(edgeTransaction)
482
+ this.log.warn('updateTransaction:' + edgeTransaction.txid)
483
+ }
484
+
485
+ // *************************************
486
+ // Save the wallet data store
487
+ // *************************************
488
+ async saveWalletLoop() {
489
+ const disklet = this.walletLocalDisklet
490
+ const promises = []
491
+ if (this.transactionListDirty) {
492
+ await this.loadTransactions()
493
+ this.log('transactionListDirty. Saving...')
494
+ let jsonString = JSON.stringify(this.transactionList)
495
+ promises.push(
496
+ disklet.setText(TRANSACTION_STORE_FILE, jsonString).catch(e => {
497
+ this.log.error('Error saving transactionList')
498
+ this.log.error(e)
499
+ })
500
+ )
501
+ jsonString = JSON.stringify(this.txIdList)
502
+ promises.push(
503
+ disklet.setText(TXID_LIST_FILE, jsonString).catch(e => {
504
+ this.log.error('Error saving txIdList')
505
+ this.log.error(e)
506
+ })
507
+ )
508
+ jsonString = JSON.stringify(this.txIdMap)
509
+ promises.push(
510
+ disklet.setText(TXID_MAP_FILE, jsonString).catch(e => {
511
+ this.log.error('Error saving txIdMap')
512
+ this.log.error(e)
513
+ })
514
+ )
515
+ await Promise.all(promises)
516
+ this.transactionListDirty = false
517
+ }
518
+ if (this.walletLocalDataDirty) {
519
+ this.log('walletLocalDataDirty. Saving...')
520
+ const jsonString = JSON.stringify(this.walletLocalData)
521
+ await disklet
522
+ .setText(DATA_STORE_FILE, jsonString)
523
+ .then(() => {
524
+ this.walletLocalDataDirty = false
525
+ })
526
+ .catch(e => {
527
+ this.log.error('Error saving walletLocalData')
528
+ this.log.error(e)
529
+ })
530
+ }
531
+ }
532
+
533
+ doInitialBalanceCallback() {
534
+ for (const currencyCode of this.walletLocalData.enabledTokens) {
535
+ try {
536
+ this.currencyEngineCallbacks.onBalanceChanged(
537
+ currencyCode,
538
+ this.walletLocalData.totalBalances[currencyCode]
539
+ )
540
+ } catch (e) {
541
+ this.log.error(
542
+ 'doInitialBalanceCallback Error for currencyCode',
543
+ currencyCode,
544
+ e
545
+ )
546
+ }
547
+ }
548
+ }
549
+
550
+ doInitialTransactionsCallback() {
551
+ for (const currencyCode of this.walletLocalData.enabledTokens) {
552
+ try {
553
+ this.currencyEngineCallbacks.onTransactionsChanged(
554
+ this.transactionList[currencyCode]
555
+ )
556
+ } catch (e) {
557
+ this.log.error(
558
+ 'doInitialTransactionsCallback Error for currencyCode',
559
+ currencyCode,
560
+ e
561
+ )
562
+ }
563
+ }
564
+ }
565
+
566
+ async addToLoop(func, timer) {
567
+ try {
568
+ // $FlowFixMe
569
+ await this[func]()
570
+ } catch (e) {
571
+ this.log.error('Error in Loop:', func, e)
572
+ }
573
+ if (this.engineOn) {
574
+ this.timers[func] = setTimeout(() => {
575
+ if (this.engineOn) {
576
+ this.addToLoop(func, timer)
577
+ }
578
+ }, timer)
579
+ }
580
+ return true
581
+ }
582
+
583
+ getTokenInfo(token) {
584
+ return this.allTokens.find(element => {
585
+ return element.currencyCode === token
586
+ })
587
+ }
588
+
589
+ updateOnAddressesChecked() {
590
+ if (this.addressesChecked) {
591
+ return
592
+ }
593
+
594
+ const activeTokens = this.walletLocalData.enabledTokens
595
+ const perTokenSlice = 1 / activeTokens.length
596
+ let totalStatus = 0
597
+ let numComplete = 0
598
+ for (const token of activeTokens) {
599
+ const balanceStatus = this.tokenCheckBalanceStatus[token] || 0
600
+ const txStatus = this.tokenCheckTransactionsStatus[token] || 0
601
+ totalStatus += ((balanceStatus + txStatus) / 2) * perTokenSlice
602
+ if (balanceStatus === 1 && txStatus === 1) {
603
+ numComplete++
604
+ }
605
+ }
606
+ if (numComplete === activeTokens.length) {
607
+ totalStatus = 1
608
+ this.addressesChecked = true
609
+ }
610
+ this.log(`${this.walletInfo.id} syncRatio of: ${totalStatus}`)
611
+ // note that sometimes callback does not get triggered on Android debug
612
+ this.currencyEngineCallbacks.onAddressesChecked(totalStatus)
613
+ }
614
+
615
+ async startEngine() {
616
+ this.addToLoop('saveWalletLoop', SAVE_DATASTORE_MILLISECONDS)
617
+ }
618
+
619
+ // *************************************
620
+ // Public methods
621
+ // *************************************
622
+
623
+ async killEngine() {
624
+ // Set status flag to false
625
+ this.engineOn = false
626
+ // Clear Inner loops timers
627
+ for (const timer in this.timers) {
628
+ clearTimeout(this.timers[timer])
629
+ }
630
+ this.timers = {}
631
+ }
632
+
633
+ async changeUserSettings(userSettings) {
634
+ this.currentSettings = userSettings
635
+ }
636
+
637
+ async clearBlockchainCache() {
638
+ const temp = JSON.stringify({
639
+ enabledTokens: this.walletLocalData.enabledTokens,
640
+ publicKey: this.walletLocalData.publicKey
641
+ })
642
+ this.walletLocalData = new WalletLocalData(
643
+ temp,
644
+ this.currencyInfo.currencyCode
645
+ )
646
+ this.walletLocalDataDirty = true
647
+ this.addressesChecked = false
648
+ this.tokenCheckBalanceStatus = {}
649
+ this.tokenCheckTransactionsStatus = {}
650
+ this.transactionList = {}
651
+ this.txIdList = {}
652
+ this.txIdMap = {}
653
+ this.transactionListDirty = true
654
+ this.otherData = this.walletLocalData.otherData
655
+ await this.saveWalletLoop()
656
+ }
657
+
658
+ getBlockHeight() {
659
+ return parseInt(this.walletLocalData.blockHeight)
660
+ }
661
+
662
+ enableTokensSync(tokens) {
663
+ for (const token of tokens) {
664
+ if (this.walletLocalData.enabledTokens.indexOf(token) === -1) {
665
+ this.walletLocalData.enabledTokens.push(token)
666
+ this.walletLocalDataDirty = true
667
+ }
668
+ }
669
+ if (this.walletLocalDataDirty) {
670
+ this.saveWalletLoop()
671
+ }
672
+ }
673
+
674
+ async enableTokens(tokens) {
675
+ this.enableTokensSync(tokens)
676
+ }
677
+
678
+ disableTokensSync(tokens) {
679
+ for (const token of tokens) {
680
+ if (token === this.currencyInfo.currencyCode) {
681
+ continue
682
+ }
683
+ const index = this.walletLocalData.enabledTokens.indexOf(token)
684
+ if (index !== -1) {
685
+ this.walletLocalData.enabledTokens.splice(index, 1)
686
+ this.walletLocalDataDirty = true
687
+ }
688
+ }
689
+ if (this.walletLocalDataDirty) {
690
+ this.saveWalletLoop()
691
+ }
692
+ }
693
+
694
+ async disableTokens(tokens) {
695
+ this.disableTokensSync(tokens)
696
+ }
697
+
698
+ async getEnabledTokens() {
699
+ return this.walletLocalData.enabledTokens
700
+ }
701
+
702
+ async addCustomToken(obj, contractAddress) {
703
+ checkCustomToken(obj)
704
+
705
+ const tokenObj = obj
706
+ // If token is already in currencyInfo, error as it cannot be changed
707
+ for (const tk of this.currencyInfo.metaTokens) {
708
+ if (
709
+ tk.currencyCode.toLowerCase() === tokenObj.currencyCode.toLowerCase() ||
710
+ tk.currencyName.toLowerCase() === tokenObj.currencyName.toLowerCase()
711
+ ) {
712
+ throw new Error('ErrorCannotModifyToken')
713
+ }
714
+ }
715
+
716
+ // Validate the token object
717
+ if (tokenObj.currencyCode.toUpperCase() !== tokenObj.currencyCode) {
718
+ throw new Error('ErrorInvalidCurrencyCode')
719
+ }
720
+ if (tokenObj.currencyCode.length < 2 || tokenObj.currencyCode.length > 7) {
721
+ throw new Error('ErrorInvalidCurrencyCodeLength')
722
+ }
723
+ if (tokenObj.currencyName.length < 3 || tokenObj.currencyName.length > 20) {
724
+ throw new Error('ErrorInvalidCurrencyNameLength')
725
+ }
726
+ if (
727
+ bns.lt(tokenObj.multiplier, '1') ||
728
+ bns.gt(tokenObj.multiplier, '100000000000000000000000000000000')
729
+ ) {
730
+ throw new Error('ErrorInvalidMultiplier')
731
+ }
732
+
733
+ for (const tk of this.customTokens) {
734
+ if (
735
+ tk.currencyCode.toLowerCase() === tokenObj.currencyCode.toLowerCase() ||
736
+ tk.currencyName.toLowerCase() === tokenObj.currencyName.toLowerCase()
737
+ ) {
738
+ // Remove old token first then re-add it to incorporate any modifications
739
+ const idx = this.customTokens.findIndex(
740
+ element => element.currencyCode === tokenObj.currencyCode
741
+ )
742
+ if (idx !== -1) {
743
+ this.customTokens.splice(idx, 1)
744
+ }
745
+ }
746
+ }
747
+
748
+ // Create a token object for inclusion in customTokens
749
+ const denom = {
750
+ name: tokenObj.currencyCode,
751
+ multiplier: tokenObj.multiplier
752
+ }
753
+ const edgeMetaToken = {
754
+ currencyCode: tokenObj.currencyCode,
755
+ currencyName: tokenObj.currencyName,
756
+ denominations: [denom],
757
+ contractAddress: contractAddress || tokenObj.contractAddress
758
+ }
759
+
760
+ this.customTokens.push(edgeMetaToken)
761
+ this.allTokens = this.currencyInfo.metaTokens.concat(this.customTokens)
762
+ this.enableTokensSync([edgeMetaToken.currencyCode])
763
+ }
764
+
765
+ getTokenStatus(token) {
766
+ return this.walletLocalData.enabledTokens.indexOf(token) !== -1
767
+ }
768
+
769
+ getBalance(options) {
770
+ const cleanOptions = asCurrencyCodeOptions(options)
771
+ const { currencyCode = this.currencyInfo.currencyCode } = cleanOptions
772
+
773
+ if (this.walletLocalData.totalBalances[currencyCode] == null) {
774
+ return '0'
775
+ }
776
+ const nativeBalance = this.walletLocalData.totalBalances[currencyCode]
777
+ return nativeBalance
778
+ }
779
+
780
+ getNumTransactions(options) {
781
+ const cleanOptions = asCurrencyCodeOptions(options)
782
+ const { currencyCode = this.currencyInfo.currencyCode } = cleanOptions
783
+
784
+ if (this.walletLocalData.numTransactions[currencyCode] == null) {
785
+ return 0
786
+ } else {
787
+ return this.walletLocalData.numTransactions[currencyCode]
788
+ }
789
+ }
790
+
791
+ async getTransactions(
792
+ options
793
+ ) {
794
+ const cleanOptions = asCurrencyCodeOptions(options)
795
+ const { currencyCode = this.currencyInfo.currencyCode } = cleanOptions
796
+
797
+ await this.loadTransactions()
798
+
799
+ if (this.transactionList[currencyCode] == null) {
800
+ return []
801
+ }
802
+
803
+ let startIndex = 0
804
+ let startEntries = 0
805
+ if (options === null) {
806
+ return this.transactionList[currencyCode].slice(0)
807
+ }
808
+ if (options.startIndex && options.startIndex > 0) {
809
+ startIndex = options.startIndex
810
+ if (startIndex >= this.transactionList[currencyCode].length) {
811
+ startIndex = this.transactionList[currencyCode].length - 1
812
+ }
813
+ }
814
+ if (options.startEntries && options.startEntries > 0) {
815
+ startEntries = options.startEntries
816
+ if (
817
+ startEntries + startIndex >
818
+ this.transactionList[currencyCode].length
819
+ ) {
820
+ // Don't read past the end of the transactionList
821
+ startEntries = this.transactionList[currencyCode].length - startIndex
822
+ }
823
+ }
824
+
825
+ // Copy the appropriate entries from the arrayTransactions
826
+ let returnArray = []
827
+
828
+ if (startEntries) {
829
+ returnArray = this.transactionList[currencyCode].slice(
830
+ startIndex,
831
+ startEntries + startIndex
832
+ )
833
+ } else {
834
+ returnArray = this.transactionList[currencyCode].slice(startIndex)
835
+ }
836
+ return returnArray
837
+ }
838
+
839
+ getFreshAddress(options) {
840
+ return { publicAddress: this.walletLocalData.publicKey }
841
+ }
842
+
843
+ addGapLimitAddresses(addresses, options) {}
844
+
845
+ isAddressUsed(address, options) {
846
+ return false
847
+ }
848
+
849
+ dumpData() {
850
+ const dataDump = {
851
+ walletId: this.walletId.split(' - ')[0],
852
+ walletType: this.walletInfo.type,
853
+ pluginType: this.currencyInfo.pluginId,
854
+ data: {
855
+ walletLocalData: this.walletLocalData
856
+ }
857
+ }
858
+ return dataDump
859
+ }
860
+
861
+ makeSpend(edgeSpendInfo) {
862
+ checkEdgeSpendInfo(edgeSpendInfo)
863
+
864
+ for (const st of edgeSpendInfo.spendTargets) {
865
+ if (st.publicAddress === this.walletLocalData.publicKey) {
866
+ throw new SpendToSelfError()
867
+ }
868
+ }
869
+
870
+ let currencyCode = ''
871
+ if (typeof edgeSpendInfo.currencyCode === 'string') {
872
+ currencyCode = edgeSpendInfo.currencyCode
873
+ if (currencyCode !== this.currencyInfo.currencyCode) {
874
+ if (!this.getTokenStatus(currencyCode)) {
875
+ throw new Error('Error: Token not supported or enabled')
876
+ }
877
+ }
878
+ } else {
879
+ currencyCode = this.currencyInfo.currencyCode
880
+ }
881
+
882
+ const nativeBalance = this.walletLocalData.totalBalances[currencyCode]
883
+ if (!nativeBalance || bns.eq(nativeBalance, '0')) {
884
+ throw new InsufficientFundsError()
885
+ }
886
+
887
+ edgeSpendInfo.currencyCode = currencyCode
888
+ const denom = getDenomInfo(
889
+ this.currencyInfo,
890
+ currencyCode,
891
+ this.customTokens
892
+ )
893
+ if (!denom) {
894
+ throw new Error('InternalErrorInvalidCurrencyCode')
895
+ }
896
+
897
+ return { edgeSpendInfo, nativeBalance, currencyCode, denom }
898
+ }
899
+
900
+ // called by GUI after sliding to confirm
901
+ async saveTx(edgeTransaction) {
902
+ // add the transaction to disk and fire off callback (alert in GUI)
903
+ this.addTransaction(edgeTransaction.currencyCode, edgeTransaction)
904
+ this.transactionsChangedArray.forEach(tx =>
905
+ this.log.warn(
906
+ `executing back in saveTx and this.transactionsChangedArray is: ${cleanTxLogs(
907
+ tx
908
+ )}`
909
+ )
910
+ )
911
+
912
+ if (this.transactionsChangedArray.length > 0) {
913
+ this.currencyEngineCallbacks.onTransactionsChanged(
914
+ this.transactionsChangedArray
915
+ )
916
+ }
917
+ }
918
+ }