• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinterTeam / minter-js-sdk / 5122024234

pending completion
5122024234

push

github

shrpne
getPoolInfo: convert values from pip

699 of 873 branches covered (80.07%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 2 files covered. (100.0%)

1213 of 1349 relevant lines covered (89.92%)

133.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.3
/src/utils.js
1
/* eslint-disable unicorn/prevent-abbreviations */
2

3
import BN from 'bn.js';
4
import {padToEven, isHexPrefixed} from 'ethjs-util';
5
import {Big, isValidAddress, isValidPublicKeyString, isValidCheck, numberToBig, addressToString, publicToString, toBuffer, convertFromPip, COIN_MAX_MAX_SUPPLY, COIN_MIN_MAX_SUPPLY} from 'minterjs-util';
6
import {walletFromMnemonic, walletFromMnemonicAsync} from 'minterjs-wallet';
7

8

9
const BASE_COIN = {
52✔
10
    '0x01': 'BIP',
11
    '0x02': 'MNT',
12
};
13

14
/**
15
 * @param {number|string} chainId
16
 * @return {string}
17
 */
18
function normalizeChainId(chainId) {
19
    if (typeof chainId === 'string' || typeof chainId === 'number') {
32✔
20
        chainId = integerToHexString(chainId);
28✔
21
    }
22

23
    return chainId;
32✔
24
}
25

26
/**
27
 * @param {number|string} chainId
28
 * @param {string} coinSymbol
29
 * @return {boolean}
30
 */
31
export function isBaseCoinSymbol(chainId, coinSymbol) {
32
    return BASE_COIN[normalizeChainId(chainId)] === coinSymbol;
28✔
33
}
34

35
/**
36
 * @param {number|string} chainId
37
 * @return {string|undefined}
38
 */
39
export function getBaseCoinSymbol(chainId) {
40
    return BASE_COIN[normalizeChainId(chainId)];
4✔
41
}
42

43
/**
44
 * @param {number|string} coinIdOrSymbol
45
 * @return {boolean}
46
 */
47
export function isCoinId(coinIdOrSymbol) {
48
    if (typeof coinIdOrSymbol === 'number') {
201✔
49
        return true;
74✔
50
    }
51
    if (typeof coinIdOrSymbol !== 'string') {
127✔
52
        return false;
32✔
53
    }
54
    return /^[0-9]+$/.test(coinIdOrSymbol);
95✔
55
}
56

57
/**
58
 * @param {string} coin
59
 * @param {object} [options]
60
 * @param {boolean} [options.allowVersion = true]
61
 * @param {boolean} [options.allowLP = true]
62
 * @return {boolean}
63
 */
64
export function isCoinSymbol(coin, {allowVersion = true, allowLP = true} = {}) {
1,554✔
65
    if (typeof coin !== 'string') {
713✔
66
        return false;
371✔
67
    }
68
    const [ticker, version, invalidPart] = coin.split('-');
342✔
69
    if (invalidPart !== undefined) {
342✔
70
        // console.debug('invalid part found, e.g. "ABC-12-34"')
71
        return false;
3✔
72
    }
73
    // validate version
74
    if (!allowVersion && version !== undefined) {
339✔
75
        // console.debug('version is not allowed');
76
        return false;
68✔
77
    }
78
    if (version?.length === 0) {
271✔
79
        // console.debug('empty version, e.g. "ABC-"');
80
        return false;
2✔
81
    }
82
    if (version?.length > 0 && !/^\d+$/.test(version)) {
269✔
83
        // console.debug('only digits in version');
84
        return false;
3✔
85
    }
86
    // validate LP
87
    const isLP = ticker === 'LP' && version?.length > 0;
266✔
88
    if (isLP) {
266✔
89
        return allowLP;
2✔
90
    }
91
    // validate ticker
92
    if (!/[A-Z]/.test(ticker)) {
264✔
93
        // console.debug('ticker should have at least one letter');
94
        return false;
17✔
95
    }
96
    // only letters and digits in ticker
97
    return /^[A-Z0-9]{3,10}$/.test(ticker);
247✔
98
}
99

100
/**
101
 * @param {number|string} num
102
 * @return {boolean}
103
 */
104
export function isNumericInteger(num) {
105
    try {
×
106
        // `new Big()` checks for valid numeric
107
        return (new Big(num)).round().toFixed() === (new Big(num)).toFixed();
×
108
    } catch (error) {
109
        return false;
×
110
    }
111
}
112

113
/**
114
 * @param {any} value
115
 */
116
export function isValidNumber(value) {
117
    const invalid = (typeof value !== 'number' && typeof value !== 'string') || (typeof value === 'string' && value.length === 0);
20✔
118
    return !invalid;
20✔
119
}
120

121
/**
122
 * @param {number|string|ByteArray} num
123
 * @return {string}
124
 */
125
export function integerToHexString(num) {
126
    num = toInteger(num);
4,327✔
127
    // handle exponential values
128
    num = (new Big(num)).toFixed();
4,327✔
129
    // convert to hex
130
    const hexNum = (new BN(num, 10)).toString(16);
4,323✔
131
    return `0x${padToEven(hexNum)}`;
4,323✔
132
}
133

134
/**
135
 * @param {number|string|ByteArray} num
136
 * @return {string}
137
 */
138
export function toInteger(num) {
139
    if (typeof num === 'number') {
5,320✔
140
        return num.toString();
3,987✔
141
    }
142
    if (num !== undefined && num !== null && num.length > 0) {
1,333✔
143
        // handle hex prefixed string
144
        if (typeof num === 'string' && isHexPrefixed(num)) {
757!
145
            return bufferToInteger(num);
×
146
        }
147
        // handle arrays
148
        if (typeof num !== 'string') {
757!
149
            return bufferToInteger(num);
×
150
        }
151
    }
152

153
    num = Number.parseInt(num, 10);
1,333✔
154

155
    return Number.isNaN(num) ? '' : num.toString();
1,333✔
156
}
157

158
/**
159
 * @param {ByteArray} buf
160
 * @return {string}
161
 */
162
export function bufferToInteger(buf) {
163
    buf = bufferFromBytes(buf);
1,769✔
164

165
    return (new BN(buf, 16)).toString(10);
1,769✔
166
}
167

168
/**
169
 * @param {ByteArray} buf
170
 * @return {boolean|null}
171
 */
172
export function bufferToBoolean(buf) {
173
    buf = bufferFromBytes(buf);
38✔
174

175
    if (buf.toString('hex') === '01') {
38✔
176
        return true;
16✔
177
    }
178

179
    if (buf.toString('hex') === '') {
22✔
180
        return false;
21✔
181
    }
182

183
    // eslint-disable-next-line unicorn/no-null
184
    return null;
1✔
185
}
186

187
/**
188
 * @typedef {Buffer|Array|string|number|null|undefined|BN} BufferCapable
189
 */
190

191
/**
192
 * @param {BufferCapable} value
193
 * @returns {string}
194
 */
195
export function dataToInteger(value) {
196
    return bufferToInteger(toBuffer(value));
264✔
197
}
198

199
/**
200
 * @param {BufferCapable} value
201
 * @returns {string}
202
 */
203
export function dataPipToAmount(value) {
204
    return convertFromPip(bufferToInteger(toBuffer(value)));
630✔
205
}
206

207
/**
208
 * @param {BufferCapable} value
209
 * @returns {string}
210
 */
211
export function dataToAddress(value) {
212
    // use zero address
213
    // if (!value || value?.length === 0) {
214
    //     value = Buffer.alloc(20, 0);
215
    // }
216
    return addressToString(value);
73✔
217
}
218

219
/**
220
 * @param {BufferCapable} value
221
 * @returns {string}
222
 */
223
export function dataToPublicKey(value) {
224
    // use zero address
225
    // if (!value || value?.length === 0) {
226
    //     value = Buffer.alloc(32, 0);
227
    // }
228
    return publicToString(value);
104✔
229
}
230

231
/**
232
 * @param {BufferCapable} value
233
 * @returns {boolean|null}
234
 */
235
export function dataToBoolean(value) {
236
    return bufferToBoolean(toBuffer(value));
10✔
237
}
238

239
/**
240
 * @param {ByteArray} bytes
241
 * @return {Buffer}
242
 */
243
export function bufferFromBytes(bytes) {
244
    if (bytes.length === undefined) {
1,899!
245
        throw new Error('Invalid value passed as ByteArray, it should be Buffer, Uint8Array or hex string');
×
246
    }
247
    // string to Buffer
248
    if (typeof bytes === 'string') {
1,899✔
249
        bytes = bytes.replace('0x', '');
92✔
250
        return Buffer.from(bytes, 'hex');
92✔
251
    }
252
    // Uint8Array to Buffer
253
    if (!Buffer.isBuffer(bytes)) {
1,807!
254
        return Buffer.from(bytes);
×
255
    }
256

257
    // it is Buffer already
258
    return bytes;
1,807✔
259
}
260

261
/**
262
 * @param {object} obj
263
 */
264
export function proxyNestedTxData(obj) {
265
    addTxDataFields(obj);
885✔
266

267
    // proxy TxData
268
    obj.raw = obj.txData.raw;
885✔
269
    obj.serialize = obj.txData.serialize;
885✔
270
    obj.serializeToString = obj.txData.serializeToString;
885✔
271
}
272

273
/**
274
 * @param {object} txData
275
 */
276
export function addTxDataFields(txData) {
277
    Object.defineProperty(txData, 'fields', {
885✔
278
        get() {
279
            const fields = {};
217✔
280
            txData.txData._fields.forEach((key) => {
217✔
281
                if (Array.isArray(txData[key])) {
889✔
282
                    // cast multisend items to fields
283
                    fields[key] = txData[key].map((item) => item.fields || item);
116✔
284
                } else {
285
                    fields[key] = txData[key];
847✔
286
                }
287
            });
288
            return fields;
217✔
289
        },
290
        enumerable: true,
291
    });
292
}
293

294
/**
295
 * @param {string} value
296
 * @param {string} fieldName
297
 */
298
export function validateAddress(value, fieldName) {
299
    validateNotEmpty(value, fieldName);
710✔
300

301
    if (typeof value === 'string' && !isValidAddress(value)) {
710!
302
        throw new Error(`Field \`${fieldName}\` is invalid address`);
×
303
    }
304
}
305

306
/**
307
 * @param {string} value
308
 * @param {string} fieldName
309
 */
310
export function validatePublicKey(value, fieldName) {
311
    validateNotEmpty(value, fieldName);
236✔
312

313
    if (typeof value === 'string' && !isValidPublicKeyString(value)) {
236!
314
        throw new Error(`Field \`${fieldName}\` is invalid public key`);
×
315
    }
316
}
317

318
/**
319
 * @param {string} value
320
 * @param {string} fieldName
321
 */
322
export function validateCheck(value, fieldName) {
323
    validateNotEmpty(value, fieldName);
40✔
324

325
    if (typeof value === 'string' && !isValidCheck(value)) {
40!
326
        throw new Error(`Field \`${fieldName}\` is invalid check string`);
×
327
    }
328
}
329

330
/**
331
 * @param {number|string} value
332
 * @param {string} fieldName
333
 */
334
export function validateAmount(value, fieldName) {
335
    validateNotEmpty(value, fieldName);
1,869✔
336

337
    if (typeof value === 'string' || typeof value === 'number') {
1,869!
338
        let valueBig;
339
        try {
1,869✔
340
            valueBig = numberToBig(value);
1,869✔
341
        } catch (error) {
342
            throw new Error(`Field \`${fieldName}\` is invalid number`);
8✔
343
        }
344

345
        if (valueBig && valueBig.lt(0)) {
1,861!
346
            throw new Error(`Field \`${fieldName}\` has negative amount`);
×
347
        }
348
    }
349
}
350

351
/**
352
 * @param {number|string} maxSupply
353
 * @param {number|string} initialAmount
354
 */
355
export function validateMaxSupply(maxSupply, initialAmount) {
356
    validateAmount(maxSupply, 'maxSupply');
77✔
357
    if (maxSupply > COIN_MAX_MAX_SUPPLY || maxSupply < COIN_MIN_MAX_SUPPLY) {
77!
358
        throw new Error(`Field \`maxSupply\` should be between ${COIN_MIN_MAX_SUPPLY} and ${COIN_MAX_MAX_SUPPLY}`);
×
359
    }
360
    if (Number(initialAmount) > Number(maxSupply)) {
77!
361
        throw new Error('Field `initialAmount` should be less or equal of `maxSupply`');
×
362
    }
363
}
364

365
/**
366
 * @param {number|string} origValue
367
 * @param {string} fieldName
368
 */
369
export function validateUint(origValue, fieldName) {
370
    validateNotEmpty(origValue, fieldName);
3,105✔
371

372
    const value = Number(origValue);
3,105✔
373
    if (Number.isNaN(value)) {
3,105✔
374
        throw new TypeError(`Field \`${fieldName}\` is not a number. Received: ${origValue}`);
8✔
375
    }
376

377
    if (value < 0) {
3,097!
378
        throw new Error(`Field \`${fieldName}\` should be positive integer. Received: ${value}`);
×
379
    }
380

381
    if (Math.round(value) !== value) {
3,097!
382
        throw new Error(`Field \`${fieldName}\` should be integer, decimal given`);
×
383
    }
384
}
385

386
/**
387
 * @param {number|string} origValue
388
 * @param {string} fieldName
389
 */
390
export function validateUintArray(origValue, fieldName) {
391
    if (!Array.isArray(origValue)) {
81!
392
        throw new TypeError(`Field \`${fieldName}\` is not an array`);
×
393
    }
394
    origValue.forEach((coin, index) => {
81✔
395
        try {
227✔
396
            validateUint(coin, fieldName);
227✔
397
        } catch (error) {
398
            // update error message
399
            throw new Error(error.message.replace(`\`${fieldName}\``, `\`${fieldName}\` contain invalid item at index: ${index}, it `));
×
400
        }
401
    });
402
}
403

404
/**
405
 * Only validates base coin ticker. Throws on LP-* tokens and ARCHIVED-* coins with version number.
406
 * @param {string} value
407
 * @param {string} fieldName
408
 */
409
export function validateTicker(value, fieldName) {
410
    validateNotEmpty(value, fieldName);
145✔
411

412
    if (typeof value === 'string' && !isCoinSymbol(value, {allowVersion: false, allowLP: false})) {
145✔
413
        throw new Error(`Field \`${fieldName}\` is invalid coin ticker string`);
34✔
414
    }
415
}
416

417
/**
418
 * @param {any} value
419
 * @param {string} fieldName
420
 */
421
function validateNotEmpty(value, fieldName) {
422
    if (value === undefined) {
6,105!
423
        throw new TypeError(`Field \`${fieldName}\` is undefined`);
×
424
    }
425
    if (value === null) {
6,105!
426
        throw new Error(`Field \`${fieldName}\` is null`);
×
427
    }
428
    if (value === false) {
6,105!
429
        throw new Error(`Field \`${fieldName}\` is false`);
×
430
    }
431
    if (value === '') {
6,105!
432
        throw new Error(`Field \`${fieldName}\` is empty string`);
×
433
    }
434
}
435

436
/**
437
 * @param {boolean} value
438
 * @param {string} fieldName
439
 */
440
export function validateBoolean(value, fieldName) {
441
    if (typeof value !== 'boolean') {
64!
442
        throw new TypeError(`Field \`${fieldName}\` should be boolean, ${typeof value} given`);
×
443
    }
444
}
445

446
/**
447
 * @param {string} seedPhrase
448
 * @return {string}
449
 */
450
export function getPrivateKeyFromSeedPhrase(seedPhrase) {
451
    return walletFromMnemonic(seedPhrase).getPrivateKeyString();
76✔
452
}
453

454
/**
455
 * @param {string} seedPhrase
456
 * @return {Promise<string>}
457
 */
458
export function getPrivateKeyFromSeedPhraseAsync(seedPhrase) {
459
    return walletFromMnemonicAsync(seedPhrase)
2✔
460
        .then((wallet) => {
461
            return wallet.getPrivateKeyString();
2✔
462
        });
463
}
464

465
/**
466
 * Promisify setTimeout
467
 * @param {number} time - milliseconds
468
 * @return {Promise}
469
 */
470
export function wait(time) {
471
    return new Promise((resolve) => {
×
472
        setTimeout(resolve, time);
×
473
    });
474
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc