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.
- package/CHANGELOG.md +713 -0
- package/LICENSE +29 -0
- package/README.md +63 -0
- package/index.js +3 -0
- package/lib/binance/bnbEngine.js +591 -0
- package/lib/binance/bnbInfo.js +43 -0
- package/lib/binance/bnbPlugin.js +168 -0
- package/lib/binance/bnbSchema.js +83 -0
- package/lib/binance/bnbTypes.js +39 -0
- package/lib/common/engine.js +918 -0
- package/lib/common/plugin.js +152 -0
- package/lib/common/schema.js +108 -0
- package/lib/common/types.js +85 -0
- package/lib/common/utils.js +378 -0
- package/lib/eos/eosEngine.js +1216 -0
- package/lib/eos/eosInfo.js +98 -0
- package/lib/eos/eosPlugin.js +314 -0
- package/lib/eos/eosSchema.js +190 -0
- package/lib/eos/eosTypes.js +88 -0
- package/lib/eos/telosInfo.js +94 -0
- package/lib/eos/waxInfo.js +95 -0
- package/lib/ethereum/etcInfo.js +121 -0
- package/lib/ethereum/ethEngine.js +832 -0
- package/lib/ethereum/ethInfo.js +1300 -0
- package/lib/ethereum/ethMiningFees.js +157 -0
- package/lib/ethereum/ethNetwork.js +2195 -0
- package/lib/ethereum/ethPlugin.js +377 -0
- package/lib/ethereum/ethSchema.js +61 -0
- package/lib/ethereum/ethTypes.js +461 -0
- package/lib/ethereum/ftminfo.js +102 -0
- package/lib/ethereum/rskInfo.js +101 -0
- package/lib/fio/fioConst.js +38 -0
- package/lib/fio/fioEngine.js +1250 -0
- package/lib/fio/fioError.js +38 -0
- package/lib/fio/fioInfo.js +72 -0
- package/lib/fio/fioPlugin.js +486 -0
- package/lib/fio/fioSchema.js +56 -0
- package/lib/index.js +44 -0
- package/lib/pluginError.js +32 -0
- package/lib/react-native/edge-currency-accountbased.js +239635 -0
- package/lib/react-native/edge-currency-accountbased.js.map +1 -0
- package/lib/react-native-io.js +41 -0
- package/lib/stellar/stellarEngine.js +563 -0
- package/lib/stellar/stellarInfo.js +37 -0
- package/lib/stellar/stellarPlugin.js +215 -0
- package/lib/stellar/stellarSchema.js +54 -0
- package/lib/stellar/stellarTypes.js +66 -0
- package/lib/tezos/tezosEngine.js +497 -0
- package/lib/tezos/tezosInfo.js +60 -0
- package/lib/tezos/tezosPlugin.js +174 -0
- package/lib/tezos/tezosTypes.js +110 -0
- package/lib/xrp/xrpEngine.js +583 -0
- package/lib/xrp/xrpInfo.js +47 -0
- package/lib/xrp/xrpPlugin.js +229 -0
- package/lib/xrp/xrpSchema.js +74 -0
- package/lib/xrp/xrpTypes.js +38 -0
- package/package.json +139 -0
- 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
|
+
}
|