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,1250 @@
1
+ function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }//
2
+
3
+ import { FIOSDK } from '@fioprotocol/fiosdk'
4
+ import { EndPoint } from '@fioprotocol/fiosdk/lib/entities/EndPoint'
5
+ import { Transactions } from '@fioprotocol/fiosdk/lib/transactions/Transactions'
6
+ import { Constants as FioConstants } from '@fioprotocol/fiosdk/lib/utils/constants'
7
+ import { bns } from 'biggystring'
8
+ import {
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ InsufficientFundsError
17
+ } from 'edge-core-js/types'
18
+
19
+ import { CurrencyEngine } from '../common/engine.js'
20
+ import {
21
+ asyncWaterfall,
22
+ cleanTxLogs,
23
+ getDenomInfo,
24
+ promiseAny,
25
+ promiseNy,
26
+ shuffleArray,
27
+ timeout
28
+ } from '../common/utils'
29
+ import {
30
+ ACTIONS_TO_END_POINT_KEYS,
31
+ BROADCAST_ACTIONS,
32
+ HISTORY_NODE_ACTIONS,
33
+ HISTORY_NODE_OFFSET
34
+ } from './fioConst.js'
35
+ import { fioApiErrorCodes, FioError } from './fioError'
36
+ import { FioPlugin } from './fioPlugin.js'
37
+ import {
38
+
39
+
40
+ asFioHistoryNodeAction,
41
+ asGetFioName,
42
+ asHistoryResponse
43
+ } from './fioSchema.js'
44
+
45
+ const ADDRESS_POLL_MILLISECONDS = 10000
46
+ const BLOCKCHAIN_POLL_MILLISECONDS = 15000
47
+ const TRANSACTION_POLL_MILLISECONDS = 10000
48
+ const FEE_ACTION_MAP = {
49
+ addPublicAddress: {
50
+ action: 'getFeeForAddPublicAddress',
51
+ propName: 'fioAddress'
52
+ },
53
+ addPublicAddresses: {
54
+ action: 'getFeeForAddPublicAddress',
55
+ propName: 'fioAddress'
56
+ },
57
+ rejectFundsRequest: {
58
+ action: 'getFeeForRejectFundsRequest',
59
+ propName: 'payerFioAddress'
60
+ },
61
+ requestFunds: {
62
+ action: 'getFeeForNewFundsRequest',
63
+ propName: 'payeeFioAddress'
64
+ },
65
+ recordObtData: {
66
+ action: 'getFeeForRecordObtData',
67
+ propName: 'payerFioAddress'
68
+ }
69
+ }
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+ export class FioEngine extends CurrencyEngine {
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+ localDataDirty() {
94
+ this.walletLocalDataDirty = true
95
+ }
96
+
97
+ constructor(
98
+ currencyPlugin,
99
+ walletInfo,
100
+ opts,
101
+ fetchCors,
102
+ tpid
103
+ ) {
104
+ super(currencyPlugin, walletInfo, opts)
105
+ this.fetchCors = fetchCors
106
+ this.fioPlugin = currencyPlugin
107
+ this.tpid = tpid
108
+ this.recentFioFee = { publicAddress: '', fee: 0 }
109
+
110
+ this.fioSdkInit()
111
+
112
+ this.otherMethods = {
113
+ fioAction: async (actionName, params) => {
114
+ switch (actionName) {
115
+ case 'addPublicAddresses':
116
+ case 'addPublicAddress':
117
+ case 'requestFunds':
118
+ case 'rejectFundsRequest': {
119
+ const { fee } = await this.multicastServers(
120
+ FEE_ACTION_MAP[actionName].action,
121
+ {
122
+ [FEE_ACTION_MAP[actionName].propName]:
123
+ params[FEE_ACTION_MAP[actionName].propName]
124
+ }
125
+ )
126
+ params.maxFee = fee
127
+ break
128
+ }
129
+ case 'recordObtData': {
130
+ const { fee } = await this.multicastServers(
131
+ FEE_ACTION_MAP[actionName].action,
132
+ {
133
+ [FEE_ACTION_MAP[actionName].propName]:
134
+ params[FEE_ACTION_MAP[actionName].propName]
135
+ }
136
+ )
137
+ params.maxFee = fee
138
+
139
+ if (params.fioRequestId) {
140
+ this.walletLocalData.otherData.fioRequestsToApprove[
141
+ params.fioRequestId
142
+ ] = params
143
+ this.localDataDirty()
144
+ const res = await this.multicastServers(actionName, params)
145
+ if (res && res.status === 'sent_to_blockchain') {
146
+ delete this.walletLocalData.otherData.fioRequestsToApprove[
147
+ params.fioRequestId
148
+ ]
149
+ this.localDataDirty()
150
+ }
151
+ return res
152
+ }
153
+ break
154
+ }
155
+ case 'renewFioAddress': {
156
+ const { fee } = await this.multicastServers('getFee', {
157
+ endPoint: EndPoint[actionName]
158
+ })
159
+ params.maxFee = fee
160
+ const res = await this.multicastServers(actionName, params)
161
+ const renewedAddress =
162
+ this.walletLocalData.otherData.fioAddresses.find(
163
+ ({ name }) => name === params.fioAddress
164
+ )
165
+ if (renewedAddress) {
166
+ renewedAddress.expiration = res.expiration
167
+ this.localDataDirty()
168
+ }
169
+ return res
170
+ }
171
+ case 'registerFioAddress': {
172
+ const { fee } = await this.multicastServers('getFee', {
173
+ endPoint: EndPoint[actionName]
174
+ })
175
+ params.maxFee = fee
176
+ const res = await this.multicastServers(actionName, params)
177
+ if (
178
+ params.ownerPublicKey &&
179
+ params.ownerPublicKey !== this.walletInfo.keys.publicKey
180
+ ) {
181
+ return {
182
+ expiration: res.expiration,
183
+ feeCollected: res.fee_collected
184
+ }
185
+ }
186
+ const addressAlreadyAdded =
187
+ this.walletLocalData.otherData.fioAddresses.find(
188
+ ({ name }) => name === params.fioAddress
189
+ )
190
+ if (!addressAlreadyAdded) {
191
+ this.walletLocalData.otherData.fioAddresses.push({
192
+ name: params.fioAddress,
193
+ expiration: res.expiration
194
+ })
195
+ this.localDataDirty()
196
+ }
197
+ return res
198
+ }
199
+ case 'renewFioDomain': {
200
+ const { fee } = await this.multicastServers('getFee', {
201
+ endPoint: EndPoint[actionName]
202
+ })
203
+ params.maxFee = fee
204
+ const res = await this.multicastServers(actionName, params)
205
+ const renewedDomain =
206
+ this.walletLocalData.otherData.fioDomains.find(
207
+ ({ name }) => name === params.fioDomain
208
+ )
209
+ if (renewedDomain) {
210
+ renewedDomain.expiration = res.expiration
211
+ this.localDataDirty()
212
+ }
213
+ return res
214
+ }
215
+ case 'registerFioDomain': {
216
+ const { fee } = await this.multicastServers('getFee', {
217
+ endPoint: EndPoint.registerFioDomain
218
+ })
219
+ params.max_fee = fee
220
+ const res = await this.multicastServers('pushTransaction', {
221
+ action: 'regdomain',
222
+ account: '',
223
+ data: {
224
+ ...params,
225
+ tpid
226
+ }
227
+ })
228
+ return res
229
+ }
230
+ case 'transferFioDomain': {
231
+ const res = await this.multicastServers(actionName, params)
232
+ const transferredDomainIndex =
233
+ this.walletLocalData.otherData.fioDomains.findIndex(
234
+ ({ name }) => name === params.fioDomain
235
+ )
236
+ if (transferredDomainIndex) {
237
+ this.walletLocalData.otherData.fioDomains.splice(
238
+ transferredDomainIndex,
239
+ 1
240
+ )
241
+ this.localDataDirty()
242
+ }
243
+ return res
244
+ }
245
+ case 'transferFioAddress': {
246
+ const res = await this.multicastServers(actionName, params)
247
+ const transferredAddressIndex =
248
+ this.walletLocalData.otherData.fioAddresses.findIndex(
249
+ ({ name }) => name === params.fioAddress
250
+ )
251
+ if (transferredAddressIndex) {
252
+ this.walletLocalData.otherData.fioAddresses.splice(
253
+ transferredAddressIndex,
254
+ 1
255
+ )
256
+ this.localDataDirty()
257
+ }
258
+ return res
259
+ }
260
+ }
261
+
262
+ return this.multicastServers(actionName, params)
263
+ },
264
+ getFee: async (
265
+ actionName,
266
+ fioAddress = ''
267
+ ) => {
268
+ const { fee } = await this.multicastServers('getFee', {
269
+ endPoint: EndPoint[actionName],
270
+ fioAddress
271
+ })
272
+ return fee
273
+ },
274
+ getFioAddresses: async (
275
+
276
+ ) => {
277
+ return this.walletLocalData.otherData.fioAddresses
278
+ },
279
+ getFioAddressNames: async () => {
280
+ return this.walletLocalData.otherData.fioAddresses.map(
281
+ fioAddress => fioAddress.name
282
+ )
283
+ },
284
+ getFioDomains: async (
285
+
286
+ ) => {
287
+ return this.walletLocalData.otherData.fioDomains
288
+ },
289
+ getApproveNeededFioRequests: async (
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+ ) => {
304
+ return this.walletLocalData.otherData.fioRequestsToApprove
305
+ }
306
+ }
307
+ }
308
+
309
+ // Normalize date if not exists "Z" parameter
310
+ getUTCDate(dateString) {
311
+ const date = new Date(dateString)
312
+
313
+ return Date.UTC(
314
+ date.getFullYear(),
315
+ date.getMonth(),
316
+ date.getDate(),
317
+ date.getHours(),
318
+ date.getMinutes(),
319
+ date.getSeconds()
320
+ )
321
+ }
322
+
323
+ async loadEngine(
324
+ plugin,
325
+ walletInfo,
326
+ opts
327
+ ) {
328
+ await super.loadEngine(plugin, walletInfo, opts)
329
+ if (typeof this.walletInfo.keys.ownerPublicKey !== 'string') {
330
+ if (walletInfo.keys.ownerPublicKey) {
331
+ this.walletInfo.keys.ownerPublicKey = walletInfo.keys.ownerPublicKey
332
+ } else {
333
+ const pubKeys = await plugin.derivePublicKey(this.walletInfo)
334
+ this.walletInfo.keys.ownerPublicKey = pubKeys.ownerPublicKey
335
+ }
336
+ }
337
+
338
+ await this.checkAbiAccounts()
339
+ }
340
+
341
+ fioSdkInit() {
342
+ const baseUrl = shuffleArray(
343
+ this.currencyInfo.defaultSettings.apiUrls.map(apiUrl => apiUrl)
344
+ )[0]
345
+
346
+ this.fioSdk = new FIOSDK(
347
+ this.walletInfo.keys.fioKey,
348
+ this.walletInfo.keys.publicKey,
349
+ baseUrl,
350
+ this.fetchCors,
351
+ undefined,
352
+ this.tpid
353
+ )
354
+ this.fioSdkPreparedTrx = new FIOSDK(
355
+ this.walletInfo.keys.fioKey,
356
+ this.walletInfo.keys.publicKey,
357
+ '',
358
+ this.fetchCors,
359
+ undefined,
360
+ this.tpid,
361
+ true
362
+ )
363
+ }
364
+
365
+ async checkAbiAccounts() {
366
+ if (Transactions.abiMap.size === FioConstants.rawAbiAccountName.length)
367
+ return
368
+ await asyncWaterfall(
369
+ shuffleArray(
370
+ this.currencyInfo.defaultSettings.apiUrls.map(
371
+ apiUrl => () => this.loadAbiAccounts(apiUrl)
372
+ )
373
+ )
374
+ )
375
+ }
376
+
377
+ async loadAbiAccounts(apiUrl) {
378
+ this.setFioSdkBaseUrl(apiUrl)
379
+ for (const accountName of FioConstants.rawAbiAccountName) {
380
+ if (Transactions.abiMap.get(accountName)) continue
381
+ const response = await this.fioSdk.getAbi(accountName)
382
+ Transactions.abiMap.set(response.account_name, response)
383
+ }
384
+ }
385
+
386
+ setFioSdkBaseUrl(apiUrl) {
387
+ Transactions.baseUrl = apiUrl
388
+ }
389
+
390
+ // Poll on the blockheight
391
+ async checkBlockchainInnerLoop() {
392
+ try {
393
+ const info = await this.multicastServers('getChainInfo')
394
+ const blockHeight = info.head_block_num
395
+ if (this.walletLocalData.blockHeight !== blockHeight) {
396
+ this.checkDroppedTransactionsThrottled()
397
+ this.walletLocalData.blockHeight = blockHeight
398
+ this.localDataDirty()
399
+ this.currencyEngineCallbacks.onBlockHeightChanged(
400
+ this.walletLocalData.blockHeight
401
+ )
402
+ }
403
+ } catch (e) {
404
+ this.log.error(`checkBlockchainInnerLoop Error fetching height: ${e}`)
405
+ }
406
+ }
407
+
408
+ getBalance(options) {
409
+ return super.getBalance(options)
410
+ }
411
+
412
+ updateBalance(tk, balance) {
413
+ if (typeof this.walletLocalData.totalBalances[tk] === 'undefined') {
414
+ this.walletLocalData.totalBalances[tk] = '0'
415
+ }
416
+ if (!bns.eq(balance, this.walletLocalData.totalBalances[tk])) {
417
+ this.walletLocalData.totalBalances[tk] = balance
418
+ this.localDataDirty()
419
+ this.log.warn(tk + ': token Address balance: ' + balance)
420
+ this.currencyEngineCallbacks.onBalanceChanged(tk, balance)
421
+ }
422
+ this.tokenCheckBalanceStatus[tk] = 1
423
+ this.updateOnAddressesChecked()
424
+ }
425
+
426
+ processTransaction(action, actor) {
427
+ const {
428
+ act: { name: trxName, data }
429
+ } = action.action_trace
430
+ let nativeAmount
431
+ let actorSender
432
+ let networkFee = '0'
433
+ let otherParams
434
+
435
+
436
+ = {}
437
+ const currencyCode = this.currencyInfo.currencyCode
438
+ const ourReceiveAddresses = []
439
+ if (action.block_num <= this.walletLocalData.otherData.highestTxHeight) {
440
+ return action.block_num
441
+ }
442
+ if (trxName !== 'trnsfiopubky' && trxName !== 'transfer') {
443
+ return action.block_num
444
+ }
445
+
446
+ // Transfer funds transaction
447
+ if (trxName === 'trnsfiopubky' && data.amount != null) {
448
+ nativeAmount = data.amount.toString()
449
+ actorSender = data.actor
450
+ if (data.payee_public_key === this.walletInfo.keys.publicKey) {
451
+ ourReceiveAddresses.push(this.walletInfo.keys.publicKey)
452
+ if (actorSender === actor) {
453
+ nativeAmount = '0'
454
+ }
455
+ } else {
456
+ nativeAmount = `-${nativeAmount}`
457
+ }
458
+
459
+ const index = this.findTransaction(
460
+ currencyCode,
461
+ action.action_trace.trx_id
462
+ )
463
+ // Check if fee transaction have already added
464
+ if (index > -1) {
465
+ const existingTrx = this.transactionList[currencyCode][index]
466
+ otherParams = { ...existingTrx.otherParams }
467
+ if (bns.gte(nativeAmount, '0')) {
468
+ return action.block_num
469
+ }
470
+ if (otherParams.isTransferProcessed) {
471
+ return action.block_num
472
+ }
473
+ if (otherParams.isFeeProcessed) {
474
+ nativeAmount = bns.sub(nativeAmount, existingTrx.networkFee)
475
+ networkFee = existingTrx.networkFee
476
+ } else {
477
+ this.log.error(
478
+ 'processTransaction error - existing spend transaction should have isTransferProcessed or isFeeProcessed set'
479
+ )
480
+ }
481
+ }
482
+ otherParams.isTransferProcessed = true
483
+
484
+ const edgeTransaction = {
485
+ txid: action.action_trace.trx_id,
486
+ date: this.getUTCDate(action.block_time) / 1000,
487
+ currencyCode,
488
+ blockHeight: action.block_num > 0 ? action.block_num : 0,
489
+ nativeAmount,
490
+ networkFee,
491
+ parentNetworkFee: '0',
492
+ ourReceiveAddresses,
493
+ signedTx: '',
494
+ otherParams
495
+ }
496
+ this.addTransaction(currencyCode, edgeTransaction)
497
+ }
498
+
499
+ // Fee transaction
500
+ if (trxName === 'transfer' && data.quantity != null) {
501
+ const [amount] = data.quantity.split(' ')
502
+ const exchangeAmount = amount.toString()
503
+ const denom = getDenomInfo(this.currencyInfo, currencyCode)
504
+ if (!denom) {
505
+ this.log.error(`Received unsupported currencyCode: ${currencyCode}`)
506
+ return 0
507
+ }
508
+ const fioAmount = bns.mul(exchangeAmount, denom.multiplier)
509
+ if (data.to === actor) {
510
+ nativeAmount = `${fioAmount}`
511
+ } else {
512
+ nativeAmount = `-${fioAmount}`
513
+ networkFee = fioAmount
514
+ }
515
+
516
+ const index = this.findTransaction(
517
+ currencyCode,
518
+ action.action_trace.trx_id
519
+ )
520
+ // Check if transfer transaction have already added
521
+ if (index > -1) {
522
+ const existingTrx = this.transactionList[currencyCode][index]
523
+ otherParams = { ...existingTrx.otherParams }
524
+ if (bns.gte(existingTrx.nativeAmount, '0')) {
525
+ return action.block_num
526
+ }
527
+ if (otherParams.isFeeProcessed) {
528
+ return action.block_num
529
+ }
530
+ if (otherParams.isTransferProcessed) {
531
+ nativeAmount = bns.sub(existingTrx.nativeAmount, networkFee)
532
+ } else {
533
+ this.log.error(
534
+ 'processTransaction error - existing spend transaction should have isTransferProcessed or isFeeProcessed set'
535
+ )
536
+ }
537
+ }
538
+
539
+ otherParams.isFeeProcessed = true
540
+ const edgeTransaction = {
541
+ txid: action.action_trace.trx_id,
542
+ date: this.getUTCDate(action.block_time) / 1000,
543
+ currencyCode,
544
+ blockHeight: action.block_num > 0 ? action.block_num : 0,
545
+ nativeAmount,
546
+ networkFee,
547
+ signedTx: '',
548
+ ourReceiveAddresses: [],
549
+ otherParams
550
+ }
551
+ this.addTransaction(currencyCode, edgeTransaction)
552
+ }
553
+
554
+ return action.block_num
555
+ }
556
+
557
+ async checkTransactions(historyNodeIndex = 0) {
558
+ if (!this.currencyInfo.defaultSettings.historyNodeUrls[historyNodeIndex])
559
+ return false
560
+ let newHighestTxHeight = this.walletLocalData.otherData.highestTxHeight
561
+ let lastActionSeqNumber = 0
562
+ const actor = this.fioSdk.transactions.getActor(
563
+ this.walletInfo.keys.publicKey
564
+ )
565
+ try {
566
+ const lastActionObject = await this.requestHistory(
567
+ historyNodeIndex,
568
+ {
569
+ account_name: actor,
570
+ pos: -1,
571
+ offset: -1
572
+ },
573
+ HISTORY_NODE_ACTIONS.getActions
574
+ )
575
+
576
+ if (lastActionObject.error && lastActionObject.error.noNodeForIndex) {
577
+ // no more history nodes left
578
+ return false
579
+ }
580
+
581
+ asHistoryResponse(lastActionObject)
582
+ if (lastActionObject.actions.length) {
583
+ lastActionSeqNumber = lastActionObject.actions[0].account_action_seq
584
+ } else {
585
+ // if no transactions at all
586
+ return true
587
+ }
588
+ } catch (e) {
589
+ return this.checkTransactions(++historyNodeIndex)
590
+ }
591
+
592
+ let pos = lastActionSeqNumber
593
+ let finish = false
594
+
595
+ while (!finish) {
596
+ if (pos < 0) {
597
+ break
598
+ }
599
+ let actionsObject
600
+ try {
601
+ actionsObject = await this.requestHistory(
602
+ historyNodeIndex,
603
+ {
604
+ account_name: actor,
605
+ pos,
606
+ offset: -HISTORY_NODE_OFFSET + 1
607
+ },
608
+ HISTORY_NODE_ACTIONS.getActions
609
+ )
610
+ if (actionsObject.error && actionsObject.error.noNodeForIndex) {
611
+ return false
612
+ }
613
+
614
+ let actions = []
615
+
616
+ if (actionsObject.actions && actionsObject.actions.length > 0) {
617
+ actions = actionsObject.actions
618
+ } else {
619
+ break
620
+ }
621
+
622
+ for (let i = actions.length - 1; i > -1; i--) {
623
+ const action = actions[i]
624
+ asFioHistoryNodeAction(action)
625
+ const blockNum = this.processTransaction(action, actor)
626
+
627
+ if (blockNum > newHighestTxHeight) {
628
+ newHighestTxHeight = blockNum
629
+ } else if (
630
+ (blockNum === newHighestTxHeight &&
631
+ i === HISTORY_NODE_OFFSET - 1) ||
632
+ blockNum < this.walletLocalData.otherData.highestTxHeight
633
+ ) {
634
+ finish = true
635
+ break
636
+ }
637
+ }
638
+
639
+ if (!actions.length || actions.length < HISTORY_NODE_OFFSET) {
640
+ break
641
+ }
642
+ pos -= HISTORY_NODE_OFFSET
643
+ } catch (e) {
644
+ return this.checkTransactions(++historyNodeIndex)
645
+ }
646
+ }
647
+ if (newHighestTxHeight > this.walletLocalData.otherData.highestTxHeight) {
648
+ this.walletLocalData.otherData.highestTxHeight = newHighestTxHeight
649
+ this.localDataDirty()
650
+ }
651
+ return true
652
+ }
653
+
654
+ async checkTransactionsInnerLoop() {
655
+ let transactions
656
+ try {
657
+ transactions = await this.checkTransactions()
658
+ } catch (e) {
659
+ this.log.error('checkTransactionsInnerLoop fetches failed with error: ')
660
+ this.log.error(e)
661
+ return false
662
+ }
663
+
664
+ if (transactions) {
665
+ this.tokenCheckTransactionsStatus.FIO = 1
666
+ this.updateOnAddressesChecked()
667
+ }
668
+ if (this.transactionsChangedArray.length > 0) {
669
+ this.currencyEngineCallbacks.onTransactionsChanged(
670
+ this.transactionsChangedArray
671
+ )
672
+ this.transactionsChangedArray = []
673
+ }
674
+ }
675
+
676
+ async requestHistory(
677
+ nodeIndex,
678
+ params
679
+
680
+
681
+
682
+ ,
683
+ uri
684
+ ) {
685
+ if (!this.currencyInfo.defaultSettings.historyNodeUrls[nodeIndex])
686
+ return { error: { noNodeForIndex: true } }
687
+ const apiUrl = this.currencyInfo.defaultSettings.historyNodeUrls[nodeIndex]
688
+ const result = await this.fetchCors(`${apiUrl}history/${uri || ''}`, {
689
+ method: 'POST',
690
+ headers: {
691
+ Accept: 'application/json',
692
+ 'Content-Type': 'application/json'
693
+ },
694
+ body: JSON.stringify(params)
695
+ })
696
+ return result.json()
697
+ }
698
+
699
+ async fioApiRequest(
700
+ apiUrl,
701
+ actionName,
702
+ params,
703
+ returnPreparedTrx = false
704
+ ) {
705
+ const fioSdk = returnPreparedTrx ? this.fioSdkPreparedTrx : this.fioSdk
706
+ this.setFioSdkBaseUrl(apiUrl)
707
+
708
+ let res
709
+
710
+ try {
711
+ switch (actionName) {
712
+ case 'getChainInfo':
713
+ res = await fioSdk.transactions.getChainInfo()
714
+ break
715
+ default:
716
+ res = await fioSdk.genericAction(actionName, params)
717
+ }
718
+ } catch (e) {
719
+ // handle FIO API error
720
+ if (e.errorCode && fioApiErrorCodes.indexOf(e.errorCode) > -1) {
721
+ if (
722
+ e.json &&
723
+ e.json.fields &&
724
+ e.json.fields[0] &&
725
+ e.json.fields[0].error
726
+ ) {
727
+ e.message = e.json.fields[0].error
728
+ }
729
+ res = {
730
+ isError: true,
731
+ data: {
732
+ code: e.errorCode,
733
+ message: e.message,
734
+ json: e.json,
735
+ list: e.list
736
+ }
737
+ }
738
+ if (e.errorCode !== 404)
739
+ this.log(
740
+ `fioApiRequest error. actionName: ${actionName} - apiUrl: ${apiUrl} - message: ${JSON.stringify(
741
+ e.json
742
+ )}`
743
+ )
744
+ } else {
745
+ this.log(
746
+ `fioApiRequest error. actionName: ${actionName} - apiUrl: ${apiUrl} - message: ${e.message}`
747
+ )
748
+ throw e
749
+ }
750
+ }
751
+
752
+ return res
753
+ }
754
+
755
+ async executePreparedTrx(
756
+ apiUrl,
757
+ endpoint,
758
+ preparedTrx
759
+ ) {
760
+ this.setFioSdkBaseUrl(apiUrl)
761
+ let res
762
+
763
+ this.log.warn(
764
+ `executePreparedTrx. preparedTrx: ${JSON.stringify(
765
+ preparedTrx
766
+ )} - apiUrl: ${apiUrl}`
767
+ )
768
+ try {
769
+ res = await this.fioSdk.executePreparedTrx(endpoint, preparedTrx)
770
+ this.log.warn(
771
+ `executePreparedTrx. res: ${JSON.stringify(
772
+ res
773
+ )} - apiUrl: ${apiUrl} - endpoint: ${endpoint}`
774
+ )
775
+ } catch (e) {
776
+ // handle FIO API error
777
+ if (e.errorCode && fioApiErrorCodes.indexOf(e.errorCode) > -1) {
778
+ this.log(
779
+ `executePreparedTrx error. requestParams: ${JSON.stringify(
780
+ preparedTrx
781
+ )} - apiUrl: ${apiUrl} - endpoint: ${endpoint} - message: ${JSON.stringify(
782
+ e.json
783
+ )}`
784
+ )
785
+ if (
786
+ e.json &&
787
+ e.json.fields &&
788
+ e.json.fields[0] &&
789
+ e.json.fields[0].error
790
+ ) {
791
+ e.message = e.json.fields[0].error
792
+ }
793
+ throw e
794
+ } else {
795
+ this.log(
796
+ `executePreparedTrx error. requestParams: ${JSON.stringify(
797
+ preparedTrx
798
+ )} - apiUrl: ${apiUrl} - endpoint: ${endpoint} - message: ${
799
+ e.message
800
+ }`
801
+ )
802
+ throw e
803
+ }
804
+ }
805
+
806
+ return res
807
+ }
808
+
809
+ async multicastServers(actionName, params) {
810
+ let res
811
+ if (BROADCAST_ACTIONS[actionName]) {
812
+ this.log.warn(
813
+ `multicastServers prepare trx. actionName: ${actionName} - res: ${JSON.stringify(
814
+ params
815
+ )}`
816
+ )
817
+ const preparedTrx = await asyncWaterfall(
818
+ shuffleArray(
819
+ this.currencyInfo.defaultSettings.apiUrls.map(
820
+ apiUrl => () => this.fioApiRequest(apiUrl, actionName, params, true)
821
+ )
822
+ )
823
+ )
824
+ this.log.warn(
825
+ `multicastServers executePreparedTrx. actionName: ${actionName} - res: ${JSON.stringify(
826
+ preparedTrx
827
+ )}`
828
+ )
829
+ res = await promiseAny(
830
+ shuffleArray(
831
+ this.currencyInfo.defaultSettings.apiUrls.map(apiUrl =>
832
+ this.executePreparedTrx(
833
+ apiUrl,
834
+ EndPoint[ACTIONS_TO_END_POINT_KEYS[actionName]],
835
+ preparedTrx
836
+ )
837
+ )
838
+ )
839
+ )
840
+ this.log.warn(
841
+ `multicastServers res. actionName: ${actionName} - res: ${JSON.stringify(
842
+ res
843
+ )}`
844
+ )
845
+ if (!res) {
846
+ throw new Error('Service is unavailable')
847
+ }
848
+ } else if (actionName === 'getFioNames') {
849
+ res = await promiseNy(
850
+ this.currencyInfo.defaultSettings.apiUrls.map(apiUrl =>
851
+ timeout(this.fioApiRequest(apiUrl, actionName, params), 10000)
852
+ ),
853
+ (result) => {
854
+ try {
855
+ return JSON.stringify(asGetFioName(result))
856
+ } catch (e) {
857
+ this.log(
858
+ `getFioNames checkResult function returned error ${e.name} ${e.message}`
859
+ )
860
+ }
861
+ },
862
+ 2
863
+ )
864
+ } else {
865
+ res = await asyncWaterfall(
866
+ shuffleArray(
867
+ this.currencyInfo.defaultSettings.apiUrls.map(
868
+ apiUrl => () => this.fioApiRequest(apiUrl, actionName, params)
869
+ )
870
+ )
871
+ )
872
+ }
873
+
874
+ if (res.isError) {
875
+ const error = new FioError(res.errorMessage || res.data.message)
876
+ error.json = res.data.json
877
+ error.list = res.data.list
878
+ error.errorCode = res.data.code
879
+
880
+ throw error
881
+ }
882
+
883
+ return res
884
+ }
885
+
886
+ // Check all account balance and other relevant info
887
+ async checkAccountInnerLoop() {
888
+ const currencyCode = this.currencyInfo.currencyCode
889
+ let nativeAmount = '0'
890
+ if (
891
+ typeof this.walletLocalData.totalBalances[currencyCode] === 'undefined'
892
+ ) {
893
+ this.walletLocalData.totalBalances[currencyCode] = '0'
894
+ }
895
+
896
+ // Balance
897
+ try {
898
+ const { balance } = await this.multicastServers('getFioBalance')
899
+ nativeAmount = balance + ''
900
+ } catch (e) {
901
+ this.log('checkAccountInnerLoop error: ' + e)
902
+ nativeAmount = '0'
903
+ }
904
+ this.updateBalance(currencyCode, nativeAmount)
905
+
906
+ // Fio Addresses
907
+ try {
908
+ const result = await this.multicastServers('getFioNames', {
909
+ fioPublicKey: this.walletInfo.keys.publicKey
910
+ })
911
+
912
+ let isChanged = false
913
+ let areAddressesChanged = false
914
+ let areDomainsChanged = false
915
+
916
+ // check addresses
917
+ if (
918
+ result.fio_addresses.length !==
919
+ this.walletLocalData.otherData.fioAddresses.length
920
+ ) {
921
+ areAddressesChanged = true
922
+ } else {
923
+ for (const fioAddress of result.fio_addresses) {
924
+ const existedFioAddress =
925
+ this.walletLocalData.otherData.fioAddresses.find(
926
+ existedFioAddress =>
927
+ existedFioAddress.name === fioAddress.fio_address
928
+ )
929
+ if (existedFioAddress) {
930
+ if (existedFioAddress.expiration !== fioAddress.expiration) {
931
+ areAddressesChanged = true
932
+ break
933
+ }
934
+ } else {
935
+ areAddressesChanged = true
936
+ break
937
+ }
938
+ }
939
+
940
+ // check for removed / transferred addresses
941
+ if (!areAddressesChanged) {
942
+ for (const fioAddress of this.walletLocalData.otherData
943
+ .fioAddresses) {
944
+ if (
945
+ result.fio_addresses.findIndex(
946
+ item => item.fio_address === fioAddress.name
947
+ ) < 0
948
+ ) {
949
+ areAddressesChanged = true
950
+ break
951
+ }
952
+ }
953
+ }
954
+ }
955
+
956
+ // check domains
957
+ if (
958
+ result.fio_domains.length !==
959
+ this.walletLocalData.otherData.fioDomains.length
960
+ ) {
961
+ areDomainsChanged = true
962
+ } else {
963
+ for (const fioDomain of result.fio_domains) {
964
+ const existedFioDomain =
965
+ this.walletLocalData.otherData.fioDomains.find(
966
+ existedFioDomain => existedFioDomain.name === fioDomain.fio_domain
967
+ )
968
+ if (existedFioDomain) {
969
+ if (existedFioDomain.expiration !== fioDomain.expiration) {
970
+ areDomainsChanged = true
971
+ break
972
+ }
973
+ if (existedFioDomain.isPublic !== !!fioDomain.is_public) {
974
+ areDomainsChanged = true
975
+ break
976
+ }
977
+ } else {
978
+ areDomainsChanged = true
979
+ break
980
+ }
981
+ }
982
+
983
+ // check for removed / transferred domains
984
+ if (!areDomainsChanged) {
985
+ for (const fioDomain of this.walletLocalData.otherData.fioDomains) {
986
+ if (
987
+ result.fio_domains.findIndex(
988
+ item => item.fio_domain === fioDomain.name
989
+ ) < 0
990
+ ) {
991
+ areDomainsChanged = true
992
+ break
993
+ }
994
+ }
995
+ }
996
+ }
997
+
998
+ if (areAddressesChanged) {
999
+ isChanged = true
1000
+ this.walletLocalData.otherData.fioAddresses = result.fio_addresses.map(
1001
+ fioAddress => ({
1002
+ name: fioAddress.fio_address,
1003
+ expiration: fioAddress.expiration
1004
+ })
1005
+ )
1006
+ }
1007
+
1008
+ if (areDomainsChanged) {
1009
+ isChanged = true
1010
+ this.walletLocalData.otherData.fioDomains = result.fio_domains.map(
1011
+ fioDomain => ({
1012
+ name: fioDomain.fio_domain,
1013
+ expiration: fioDomain.expiration,
1014
+ isPublic: !!fioDomain.is_public
1015
+ })
1016
+ )
1017
+ }
1018
+
1019
+ if (isChanged) this.localDataDirty()
1020
+ } catch (e) {
1021
+ this.log.warn('checkAccountInnerLoop getFioNames error: ' + e)
1022
+ }
1023
+ }
1024
+
1025
+ async approveErroredFioRequests() {
1026
+ for (const fioRequestId in this.walletLocalData.otherData
1027
+ .fioRequestsToApprove) {
1028
+ try {
1029
+ await this.otherMethods.fioAction(
1030
+ 'recordObtData',
1031
+ this.walletLocalData.otherData.fioRequestsToApprove[fioRequestId]
1032
+ )
1033
+ } catch (e) {
1034
+ this.log.error(
1035
+ `approveErroredFioRequests recordObtData error: ${JSON.stringify(
1036
+ e
1037
+ )} for ${
1038
+ this.walletLocalData.otherData.fioRequestsToApprove[fioRequestId]
1039
+ }`
1040
+ )
1041
+ }
1042
+ }
1043
+ }
1044
+
1045
+ async clearBlockchainCache() {
1046
+ await super.clearBlockchainCache()
1047
+ this.walletLocalData.otherData.highestTxHeight = 0
1048
+ this.walletLocalData.otherData.fioAddresses = []
1049
+ this.walletLocalData.otherData.fioDomains = []
1050
+ this.walletLocalData.otherData.fioRequestsToApprove = {}
1051
+ }
1052
+
1053
+ // ****************************************************************************
1054
+ // Public methods
1055
+ // ****************************************************************************
1056
+
1057
+ // This routine is called once a wallet needs to start querying the network
1058
+ async startEngine() {
1059
+ this.engineOn = true
1060
+ this.addToLoop('checkBlockchainInnerLoop', BLOCKCHAIN_POLL_MILLISECONDS)
1061
+ this.addToLoop('checkAccountInnerLoop', ADDRESS_POLL_MILLISECONDS)
1062
+ this.addToLoop('checkTransactionsInnerLoop', TRANSACTION_POLL_MILLISECONDS)
1063
+ this.addToLoop('approveErroredFioRequests', ADDRESS_POLL_MILLISECONDS)
1064
+ super.startEngine()
1065
+ }
1066
+
1067
+ async resyncBlockchain() {
1068
+ await this.killEngine()
1069
+ await this.clearBlockchainCache()
1070
+ await this.startEngine()
1071
+ }
1072
+
1073
+ async makeSpend(edgeSpendInfoIn) {
1074
+ const { edgeSpendInfo, nativeBalance, currencyCode } = super.makeSpend(
1075
+ edgeSpendInfoIn
1076
+ )
1077
+
1078
+ const { otherParams } = edgeSpendInfo
1079
+ let fee
1080
+ if (_optionalChain([otherParams, 'optionalAccess', _ => _.fioAction])) {
1081
+ let feeFioAddress = ''
1082
+ if (FEE_ACTION_MAP[otherParams.fioAction] && otherParams.fioParams) {
1083
+ feeFioAddress =
1084
+ otherParams.fioParams[FEE_ACTION_MAP[otherParams.fioAction].propName]
1085
+ }
1086
+ const feeResponse = await this.multicastServers('getFee', {
1087
+ endPoint: EndPoint[otherParams.fioAction],
1088
+ fioAddress: feeFioAddress
1089
+ })
1090
+ fee = feeResponse.fee
1091
+ } else {
1092
+ // Only query FIO fee if the public address is different from last makeSpend()
1093
+ if (
1094
+ edgeSpendInfo.spendTargets[0].publicAddress ===
1095
+ this.recentFioFee.publicAddress
1096
+ ) {
1097
+ fee = this.recentFioFee.fee
1098
+ } else {
1099
+ const feeResponse = await this.multicastServers('getFee', {
1100
+ endPoint: EndPoint.transferTokens
1101
+ })
1102
+ fee = feeResponse.fee
1103
+ }
1104
+ }
1105
+
1106
+ const publicAddress = edgeSpendInfo.spendTargets[0].publicAddress
1107
+ const quantity = edgeSpendInfo.spendTargets[0].nativeAmount
1108
+ if (bns.gt(bns.add(quantity, `${fee}`), nativeBalance)) {
1109
+ throw new InsufficientFundsError()
1110
+ }
1111
+
1112
+ if (_optionalChain([otherParams, 'optionalAccess', _2 => _2.fioAction])) {
1113
+ if (
1114
+ ['transferFioAddress', 'transferFioDomain'].indexOf(
1115
+ otherParams.fioAction
1116
+ ) > -1
1117
+ ) {
1118
+ otherParams.fioParams.newOwnerKey = publicAddress
1119
+ }
1120
+ const edgeTransaction = {
1121
+ txid: '',
1122
+ date: 0,
1123
+ currencyCode: this.currencyInfo.currencyCode,
1124
+ blockHeight: 0,
1125
+ nativeAmount: `-${fee}`,
1126
+ networkFee: `${fee}`,
1127
+ parentNetworkFee: '0',
1128
+ signedTx: '',
1129
+ ourReceiveAddresses: [],
1130
+ otherParams: {
1131
+ transactionJson: otherParams
1132
+ },
1133
+ metadata: {
1134
+ notes: ''
1135
+ }
1136
+ }
1137
+
1138
+ return edgeTransaction
1139
+ } else {
1140
+ const memo = ''
1141
+ const actor = ''
1142
+ const transactionJson = {
1143
+ actions: [
1144
+ {
1145
+ account: 'fio.token',
1146
+ name: 'trnsfiopubky',
1147
+ authorization: [
1148
+ {
1149
+ actor: actor,
1150
+ permission: 'active'
1151
+ }
1152
+ ],
1153
+ data: {
1154
+ from: this.walletInfo.keys.publicKey,
1155
+ to: publicAddress,
1156
+ quantity,
1157
+ memo
1158
+ }
1159
+ }
1160
+ ]
1161
+ }
1162
+
1163
+ const edgeTransaction = {
1164
+ txid: '', // txid
1165
+ date: 0, // date
1166
+ currencyCode, // currencyCode
1167
+ blockHeight: 0, // blockHeight
1168
+ nativeAmount: bns.sub(`-${quantity}`, `${fee}`), // nativeAmount
1169
+ networkFee: `${fee}`, // networkFee
1170
+ ourReceiveAddresses: [], // ourReceiveAddresses
1171
+ signedTx: '0', // signedTx
1172
+ otherParams: {
1173
+ transactionJson
1174
+ }
1175
+ }
1176
+
1177
+ this.recentFioFee = { publicAddress, fee }
1178
+
1179
+ return edgeTransaction
1180
+ }
1181
+ }
1182
+
1183
+ async signTx(edgeTransaction) {
1184
+ // Do nothing
1185
+ return edgeTransaction
1186
+ }
1187
+
1188
+ async broadcastTx(
1189
+ edgeTransaction
1190
+ ) {
1191
+ let trx
1192
+ if (
1193
+ edgeTransaction.otherParams &&
1194
+ edgeTransaction.otherParams.transactionJson &&
1195
+ edgeTransaction.otherParams.transactionJson.fioAction
1196
+ ) {
1197
+ trx = await this.otherMethods.fioAction(
1198
+ edgeTransaction.otherParams.transactionJson.fioAction,
1199
+ edgeTransaction.otherParams.transactionJson.fioParams
1200
+ )
1201
+ edgeTransaction.metadata = {
1202
+ notes: trx.transaction_id
1203
+ }
1204
+ } else if (edgeTransaction.spendTargets) {
1205
+ // do transfer
1206
+ const publicAddress = edgeTransaction.spendTargets[0].publicAddress
1207
+ const amount = bns.abs(
1208
+ bns.add(edgeTransaction.nativeAmount, edgeTransaction.networkFee)
1209
+ )
1210
+ trx = await this.multicastServers('transferTokens', {
1211
+ payeeFioPublicKey: publicAddress,
1212
+ amount,
1213
+ maxFee: edgeTransaction.networkFee
1214
+ })
1215
+ } else {
1216
+ throw new Error(
1217
+ 'transactionJson not set. FIO transferTokens requires publicAddress'
1218
+ )
1219
+ }
1220
+
1221
+ edgeTransaction.txid = trx.transaction_id
1222
+ edgeTransaction.date = Date.now() / 1000
1223
+ edgeTransaction.blockHeight = trx.block_num
1224
+ this.log.warn(`SUCCESS broadcastTx\n${cleanTxLogs(edgeTransaction)}`)
1225
+
1226
+ return edgeTransaction
1227
+ }
1228
+
1229
+ getFreshAddress(options) {
1230
+ return { publicAddress: this.walletInfo.keys.publicKey }
1231
+ }
1232
+
1233
+ getDisplayPrivateSeed() {
1234
+ let out = ''
1235
+ if (this.walletInfo.keys && this.walletInfo.keys.fioKey) {
1236
+ out += this.walletInfo.keys.fioKey
1237
+ }
1238
+ return out
1239
+ }
1240
+
1241
+ getDisplayPublicSeed() {
1242
+ let out = ''
1243
+ if (this.walletInfo.keys && this.walletInfo.keys.publicKey) {
1244
+ out += this.walletInfo.keys.publicKey
1245
+ }
1246
+ return out
1247
+ }
1248
+ }
1249
+
1250
+ export { CurrencyEngine }