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,152 @@
1
+ /**
2
+ * Created by paul on 8/8/17.
3
+ */
4
+ //
5
+
6
+ import { bns } from 'biggystring'
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+ import { serialize } from 'uri-js'
19
+ import parse from 'url-parse'
20
+
21
+ import { getDenomInfo } from '../common/utils.js'
22
+
23
+ // TODO: pass in denoms pull code into common
24
+ export class CurrencyPlugin {
25
+
26
+
27
+
28
+
29
+
30
+ constructor(io, pluginId, currencyInfo) {
31
+ this.io = io
32
+ this.pluginId = pluginId
33
+ this.currencyInfo = currencyInfo
34
+ this.highestTxHeight = 0
35
+ }
36
+
37
+ async createPrivateKey(walletType) {
38
+ throw new Error('Must implement createPrivateKey')
39
+ }
40
+
41
+ async derivePublicKey(walletInfo) {
42
+ throw new Error('Must implement derivePublicKey')
43
+ }
44
+
45
+ async makeEngine(
46
+ walletInfo,
47
+ opts
48
+ ) {
49
+ throw new Error('Must implement makeEngine')
50
+ }
51
+
52
+ async parseUri(uri) {
53
+ throw new Error('Must implement parseUri')
54
+ }
55
+
56
+ async encodeUri(obj) {
57
+ throw new Error('Must implement encodeUri')
58
+ }
59
+
60
+ parseUriCommon(
61
+ currencyInfo,
62
+ uri,
63
+ networks,
64
+ currencyCode,
65
+ customTokens
66
+ ) {
67
+ const parsedUri = parse(uri, {}, true)
68
+
69
+ // Remove ":" from protocol
70
+ if (parsedUri.protocol) {
71
+ parsedUri.protocol = parsedUri.protocol.replace(':', '')
72
+ }
73
+
74
+ if (parsedUri.protocol && !networks[parsedUri.protocol]) {
75
+ throw new Error('InvalidUriError') // possibly scanning wrong crypto type
76
+ }
77
+
78
+ // If no host and no path, then it's not a valid URI
79
+ if (parsedUri.host === '' && parsedUri.pathname === '') {
80
+ throw new Error('InvalidUriError')
81
+ }
82
+
83
+ // Address uses the host if present to support URLs with double-slashes (//)
84
+ const publicAddress =
85
+ parsedUri.host !== '' ? parsedUri.host : parsedUri.pathname.split('/')[0]
86
+
87
+ const edgeParsedUri = {
88
+ publicAddress
89
+ }
90
+
91
+ // Metadata query parameters
92
+ const label = parsedUri.query.label
93
+ const message = parsedUri.query.message
94
+ const category = parsedUri.query.category
95
+
96
+ if (label || message || category) {
97
+ edgeParsedUri.metadata = {}
98
+ edgeParsedUri.metadata.name = label || undefined
99
+ edgeParsedUri.metadata.notes = message || undefined
100
+ edgeParsedUri.metadata.category = category || undefined
101
+ }
102
+
103
+ const amountStr = parsedUri.query.amount
104
+ if (amountStr && typeof amountStr === 'string') {
105
+ if (!currencyCode) {
106
+ currencyCode = currencyInfo.currencyCode
107
+ }
108
+ const denom = getDenomInfo(currencyInfo, currencyCode, customTokens)
109
+ if (!denom) {
110
+ throw new Error('InternalErrorInvalidCurrencyCode')
111
+ }
112
+ let nativeAmount = bns.mul(amountStr, denom.multiplier)
113
+ nativeAmount = bns.toFixed(nativeAmount, 0, 0)
114
+
115
+ edgeParsedUri.nativeAmount = nativeAmount || undefined
116
+ edgeParsedUri.currencyCode = currencyCode || undefined
117
+ }
118
+
119
+ return { edgeParsedUri, parsedUri }
120
+ }
121
+
122
+ encodeUriCommon(obj, network, amount) {
123
+ if (!obj.publicAddress) {
124
+ throw new Error('InvalidPublicAddressError')
125
+ }
126
+ if (!amount && !obj.label && !obj.message) {
127
+ return obj.publicAddress
128
+ } else {
129
+ let queryString = ''
130
+ if (amount) {
131
+ queryString += 'amount=' + amount + '&'
132
+ }
133
+ if (obj.label || obj.message) {
134
+ if (typeof obj.label === 'string') {
135
+ queryString += 'label=' + obj.label + '&'
136
+ }
137
+ if (typeof obj.message === 'string') {
138
+ queryString += 'message=' + obj.message + '&'
139
+ }
140
+ }
141
+ queryString = queryString.substr(0, queryString.length - 1)
142
+
143
+ const serializeObj = {
144
+ scheme: network,
145
+ path: obj.publicAddress,
146
+ query: queryString
147
+ }
148
+ const url = serialize(serializeObj)
149
+ return url
150
+ }
151
+ }
152
+ }
@@ -0,0 +1,108 @@
1
+ //
2
+
3
+ import { asArray, asObject, asOptional, asString } from 'cleaners'
4
+
5
+ export const CurrencyInfoSchema = {
6
+ type: 'object',
7
+ properties: {
8
+ walletTypes: {
9
+ type: 'array',
10
+ items: { type: 'string' }
11
+ },
12
+ currencyCode: { type: 'string' },
13
+ currencyName: { type: 'string' },
14
+ addressExplorer: { type: 'string' },
15
+ transactionExplorer: { type: 'string' },
16
+ defaultSettings: {
17
+ type: 'object'
18
+ },
19
+ denominations: {
20
+ type: 'array',
21
+ items: {
22
+ type: 'object',
23
+ properties: {
24
+ name: { type: 'string' },
25
+ multiplier: { type: 'string' },
26
+ symbol: { type: 'string' }
27
+ },
28
+ required: ['name', 'multiplier']
29
+ }
30
+ },
31
+ metaTokens: {
32
+ type: 'array',
33
+ items: {
34
+ type: 'object',
35
+ properties: {
36
+ currencyCode: { type: 'string' },
37
+ currencyName: { type: 'string' },
38
+ denominations: {
39
+ type: 'array',
40
+ items: {
41
+ type: 'object',
42
+ properties: {
43
+ name: { type: 'string' },
44
+ multiplier: { type: 'string' },
45
+ symbol: { type: 'string' }
46
+ },
47
+ required: ['name', 'multiplier']
48
+ }
49
+ },
50
+ contractAddress: { type: 'string' }
51
+ },
52
+ required: ['currencyCode', 'currencyName', 'denominations']
53
+ }
54
+ }
55
+ },
56
+ required: [
57
+ 'walletTypes',
58
+ 'currencyCode',
59
+ 'currencyName',
60
+ 'defaultSettings',
61
+ 'denominations',
62
+ 'addressExplorer',
63
+ 'transactionExplorer'
64
+ ]
65
+ }
66
+
67
+ export const asCurrencyCodeOptions = asObject({
68
+ currencyCode: asOptional(asString)
69
+ })
70
+
71
+ /**
72
+ * Does the same tests that the old JSON schema used to do,
73
+ * but with better error reporting.
74
+ */
75
+ export function checkEdgeSpendInfo(raw) {
76
+ try {
77
+ asPartialSpendInfo(raw)
78
+ } catch (error) {
79
+ throw new TypeError('Invalid EdgeSpendInfo: ' + error.message)
80
+ }
81
+ }
82
+
83
+ export function checkCustomToken(raw) {
84
+ try {
85
+ asCustomToken(raw)
86
+ } catch (error) {
87
+ throw new TypeError('Invalid CustomToken: ' + error.message)
88
+ }
89
+ }
90
+
91
+ const asPartialSpendInfo = asObject({
92
+ currencyCode: asOptional(asString),
93
+ networkFeeOption: asOptional(asString),
94
+ spendTargets: asArray(
95
+ asObject({
96
+ currencyCode: asOptional(asString),
97
+ publicAddress: asString,
98
+ nativeAmount: asOptional(asString, '0')
99
+ })
100
+ )
101
+ })
102
+
103
+ const asCustomToken = asObject({
104
+ currencyCode: asString,
105
+ currencyName: asString,
106
+ multiplier: asString,
107
+ contractAddress: asString
108
+ })
@@ -0,0 +1,85 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+ export const DATA_STORE_FILE = 'txEngineFolder/walletLocalData.json'
9
+ export const TXID_MAP_FILE = 'txEngineFolder/txidMap.json'
10
+ export const TXID_LIST_FILE = 'txEngineFolder/txidList.json'
11
+ export const TRANSACTION_STORE_FILE = 'txEngineFolder/transactionList.json'
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+ export class WalletLocalData {
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ constructor(jsonString, primaryCurrency) {
38
+ this.blockHeight = 0
39
+ const totalBalances = {}
40
+ this.totalBalances = totalBalances
41
+ this.lastAddressQueryHeight = 0
42
+ this.lastTransactionQueryHeight = {}
43
+ this.lastTransactionDate = {}
44
+ this.lastCheckedTxsDropped = 0
45
+ this.numUnconfirmedSpendTxs = 0
46
+ this.numTransactions = {}
47
+ this.otherData = {}
48
+ this.publicKey = ''
49
+ this.enabledTokens = [primaryCurrency]
50
+ if (jsonString !== null) {
51
+ const data = JSON.parse(jsonString)
52
+
53
+ if (typeof data.blockHeight === 'number') {
54
+ this.blockHeight = data.blockHeight
55
+ }
56
+ if (typeof data.lastCheckedTxsDropped === 'number') {
57
+ this.lastCheckedTxsDropped = data.lastCheckedTxsDropped
58
+ }
59
+ if (typeof data.numUnconfirmedSpendTxs === 'number') {
60
+ this.numUnconfirmedSpendTxs = data.numUnconfirmedSpendTxs
61
+ }
62
+ if (typeof data.numTransactions === 'object') {
63
+ this.numTransactions = data.numTransactions
64
+ }
65
+ if (typeof data.lastAddressQueryHeight === 'number') {
66
+ this.lastAddressQueryHeight = data.lastAddressQueryHeight
67
+ }
68
+ if (typeof data.publicKey === 'string') this.publicKey = data.publicKey
69
+ if (typeof data.totalBalances !== 'undefined') {
70
+ this.totalBalances = data.totalBalances
71
+ }
72
+ if (typeof data.enabledTokens !== 'undefined') {
73
+ this.enabledTokens = data.enabledTokens
74
+ if (!this.enabledTokens.includes(primaryCurrency)) {
75
+ this.enabledTokens.push(primaryCurrency)
76
+ }
77
+ }
78
+ if (typeof data.otherData !== 'undefined') this.otherData = data.otherData
79
+ if (typeof data.lastTransactionQueryHeight === 'object')
80
+ this.lastTransactionQueryHeight = data.lastTransactionQueryHeight
81
+ if (typeof data.lastTransactionDate === 'object')
82
+ this.lastTransactionDate = data.lastTransactionDate
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Created by paul on 8/26/17.
3
+ *
4
+ */
5
+
6
+ import { bns } from 'biggystring'
7
+ import { Buffer } from 'buffer'
8
+ import { asArray, asObject, asOptional, asString } from 'cleaners'
9
+
10
+
11
+
12
+
13
+
14
+
15
+ import { validate } from 'jsonschema'
16
+
17
+ function normalizeAddress(address) {
18
+ return address.toLowerCase().replace('0x', '')
19
+ }
20
+
21
+ function addHexPrefix(value) {
22
+ if (value.indexOf('0x') === 0) {
23
+ return value
24
+ } else {
25
+ return '0x' + value
26
+ }
27
+ }
28
+
29
+ function shuffleArray(array) {
30
+ let currentIndex = array.length
31
+ let temporaryValue, randomIndex
32
+
33
+ // While there remain elements to shuffle...
34
+ while (currentIndex !== 0) {
35
+ // Pick a remaining element...
36
+ randomIndex = Math.floor(Math.random() * currentIndex)
37
+ currentIndex -= 1
38
+
39
+ // And swap it with the current element.
40
+ temporaryValue = array[currentIndex]
41
+ array[currentIndex] = array[randomIndex]
42
+ array[randomIndex] = temporaryValue
43
+ }
44
+
45
+ return array
46
+ }
47
+ function validateObject(object, schema) {
48
+ const result = validate(object, schema)
49
+
50
+ if (result.errors.length === 0) {
51
+ return true
52
+ } else {
53
+ for (const n in result.errors) {
54
+ const errMsg = result.errors[n].message
55
+ console.log('ERROR: validateObject:' + errMsg)
56
+ }
57
+ return false
58
+ }
59
+ }
60
+
61
+ export function isEmpty(map) {
62
+ return Object.keys(map).length !== 0
63
+ }
64
+
65
+ export function isHex(h) {
66
+ const out = /^[0-9A-F]+$/i.test(h)
67
+ return out
68
+ }
69
+
70
+ export function toHex(num) {
71
+ return bns.add(num, '0', 16)
72
+ }
73
+
74
+ export function hexToBuf(hex) {
75
+ const noHexPrefix = hex.replace('0x', '')
76
+ const buf = Buffer.from(noHexPrefix, 'hex')
77
+ return buf
78
+ }
79
+
80
+ export function padHex(hex, bytes) {
81
+ if (2 * bytes - hex.length > 0) {
82
+ return hex.padStart(2 * bytes, '0')
83
+ }
84
+ return hex
85
+ }
86
+
87
+ export function removeHexPrefix(value) {
88
+ if (value.indexOf('0x') === 0) {
89
+ return value.substring(2)
90
+ } else {
91
+ return value
92
+ }
93
+ }
94
+
95
+ export function hexToDecimal(num) {
96
+ return bns.add(num, '0', 10)
97
+ }
98
+
99
+ export function bufToHex(buf) {
100
+ const signedTxBuf = Buffer.from(buf)
101
+ const hex = '0x' + signedTxBuf.toString('hex')
102
+ return hex
103
+ }
104
+
105
+ function getDenomInfo(
106
+ currencyInfo,
107
+ denom,
108
+ customTokens
109
+ ) {
110
+ // Look in the primary currency denoms
111
+ let edgeDenomination = currencyInfo.denominations.find(element => {
112
+ return element.name === denom
113
+ })
114
+
115
+ // Look in the currencyInfo tokens
116
+ if (!edgeDenomination) {
117
+ for (const metaToken of currencyInfo.metaTokens) {
118
+ edgeDenomination = metaToken.denominations.find(element => {
119
+ return element.name === denom
120
+ })
121
+ if (edgeDenomination) {
122
+ break
123
+ }
124
+ }
125
+ }
126
+
127
+ // Look in custom tokens
128
+ if (!edgeDenomination && customTokens) {
129
+ for (const metaToken of customTokens) {
130
+ edgeDenomination = metaToken.denominations.find(element => {
131
+ return element.name === denom
132
+ })
133
+ if (edgeDenomination) {
134
+ break
135
+ }
136
+ }
137
+ }
138
+ return edgeDenomination
139
+ }
140
+
141
+ const snoozeReject = (ms) =>
142
+ new Promise((resolve, reject) => setTimeout(reject, ms))
143
+ const snooze = (ms) =>
144
+ new Promise((resolve) => setTimeout(resolve, ms))
145
+
146
+ function promiseAny(promises) {
147
+ return new Promise((resolve, reject) => {
148
+ let pending = promises.length
149
+ for (const promise of promises) {
150
+ promise.then(
151
+ value => {
152
+ resolve(value)
153
+ },
154
+ error => {
155
+ return --pending || reject(error)
156
+ }
157
+ )
158
+ }
159
+ })
160
+ }
161
+
162
+ /**
163
+ * Waits for the promises to resolve and uses a provided checkResult function
164
+ * to return a key to identify the result. The returned promise resolves when
165
+ * n number of promises resolve to identical keys.
166
+ */
167
+ async function promiseNy(
168
+ promises,
169
+ checkResult,
170
+ n = promises.length
171
+ ) {
172
+ const map = {}
173
+ return new Promise((resolve, reject) => {
174
+ let resolved = 0
175
+ let failed = 0
176
+ let done = false
177
+ for (const promise of promises) {
178
+ promise.then(
179
+ result => {
180
+ const key = checkResult(result)
181
+ if (key !== undefined) {
182
+ resolved++
183
+ if (map[key] !== undefined) {
184
+ map[key]++
185
+ } else {
186
+ map[key] = 1
187
+ }
188
+ if (!done && map[key] >= n) {
189
+ done = true
190
+ resolve(result)
191
+ }
192
+ } else if (++failed + resolved === promises.length) {
193
+ reject(Error('Could not resolve n promises'))
194
+ }
195
+ },
196
+ error => {
197
+ if (++failed + resolved === promises.length) {
198
+ reject(error)
199
+ }
200
+ }
201
+ )
202
+ }
203
+ })
204
+ }
205
+
206
+ /**
207
+ * If the promise doesn't resolve in the given time,
208
+ * reject it with the provided error, or a generic error if none is provided.
209
+ */
210
+ function timeout(
211
+ promise,
212
+ ms,
213
+ error = new Error(`Timeout of ${ms}ms exceeded`)
214
+ ) {
215
+ return new Promise((resolve, reject) => {
216
+ const timer = setTimeout(() => reject(error), ms)
217
+ promise.then(
218
+ ok => {
219
+ resolve(ok)
220
+ clearTimeout(timer)
221
+ },
222
+ error => {
223
+ reject(error)
224
+ clearTimeout(timer)
225
+ }
226
+ )
227
+ })
228
+ }
229
+
230
+
231
+
232
+ async function asyncWaterfall(
233
+ asyncFuncs,
234
+ timeoutMs = 5000
235
+ ) {
236
+ let pending = asyncFuncs.length
237
+ const promises = []
238
+ for (const func of asyncFuncs) {
239
+ const index = promises.length
240
+ promises.push(
241
+ func().catch(e => {
242
+ e.index = index
243
+ throw e
244
+ })
245
+ )
246
+ if (pending > 1) {
247
+ promises.push(
248
+ new Promise(resolve => {
249
+ snooze(timeoutMs).then(() => {
250
+ resolve('async_waterfall_timed_out')
251
+ })
252
+ })
253
+ )
254
+ }
255
+ try {
256
+ const result = await Promise.race(promises)
257
+ if (result === 'async_waterfall_timed_out') {
258
+ promises.pop()
259
+ --pending
260
+ } else {
261
+ return result
262
+ }
263
+ } catch (e) {
264
+ const i = e.index
265
+ promises.splice(i, 1)
266
+ promises.pop()
267
+ --pending
268
+ if (!pending) {
269
+ throw e
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ export function pickRandom(list, count) {
276
+ if (list.length <= count) return list
277
+
278
+ // Algorithm from https://stackoverflow.com/a/48089/1836596
279
+ const out = []
280
+ for (let i = 0; i < list.length && out.length < count; ++i) {
281
+ const probability = (count - out.length) / (list.length - i)
282
+ if (Math.random() <= probability) out.push(list[i])
283
+ }
284
+ return out
285
+ }
286
+
287
+ function getEdgeInfoServer() {
288
+ return 'https://info1.edgesecure.co:8444'
289
+ }
290
+
291
+ /**
292
+ * Safely read `otherParams` from a transaction, throwing if it's missing.
293
+ */
294
+ export function getOtherParams(tx) {
295
+ if (tx.otherParams == null) {
296
+ throw new TypeError('Transaction is missing otherParams')
297
+ }
298
+ return tx.otherParams
299
+ }
300
+
301
+
302
+ /**
303
+ * Constructs a mutex.
304
+ *
305
+ * The mutex is a function that accepts & runs a callback,
306
+ * ensuring that only one callback runs at a time. Use it like:
307
+ *
308
+ * const result = await mutex(() => {
309
+ * // Critical code that must not run more than one copy.
310
+ * return result
311
+ * })
312
+ */
313
+ export function makeMutex() {
314
+ let busy = false
315
+ const queue = []
316
+ return async function lock(callback) {
317
+ if (busy) await new Promise(resolve => queue.push(resolve))
318
+ try {
319
+ busy = true
320
+ return callback()
321
+ } finally {
322
+ busy = false
323
+ const resolve = queue.shift()
324
+ if (resolve != null) resolve()
325
+ }
326
+ }
327
+ }
328
+
329
+ const asCleanTxLogs = asObject({
330
+ txid: asString,
331
+ spendTargets: asOptional(
332
+ asArray(
333
+ asObject({
334
+ currencyCode: asString,
335
+ nativeAmount: asString,
336
+ publicAddress: asString,
337
+ uniqueIdentifier: asOptional(asString)
338
+ })
339
+ )
340
+ ),
341
+ signedTx: asString,
342
+ otherParams: asOptional(
343
+ asObject({
344
+ gas: asOptional(asString),
345
+ gasPrice: asOptional(asString),
346
+ nonceUsed: asOptional(asString)
347
+ })
348
+ )
349
+ })
350
+
351
+ export function cleanTxLogs(tx) {
352
+ return JSON.stringify(asCleanTxLogs(tx), null, 2)
353
+ }
354
+
355
+ // Convert number strings in scientific notation to decimal notation using biggystring
356
+ export function biggyScience(num) {
357
+ const [factor, exponent] = num.split('e')
358
+
359
+ // exit early if the number is not in scientific notation
360
+ if (exponent == null) return num
361
+
362
+ return bns.mul(factor, '1' + '0'.repeat(parseInt(exponent))).toString()
363
+ }
364
+
365
+ export {
366
+ normalizeAddress,
367
+ addHexPrefix,
368
+ validateObject,
369
+ getDenomInfo,
370
+ asyncWaterfall,
371
+ snooze,
372
+ shuffleArray,
373
+ snoozeReject,
374
+ getEdgeInfoServer,
375
+ promiseAny,
376
+ promiseNy,
377
+ timeout
378
+ }