crypto-client
Version:
An unified client for all cryptocurrency exchanges.
252 lines (251 loc) • 9.65 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectPlatform = exports.detectPlatformFromAddress = exports.calcTokenPlatform = exports.convertPriceAndQuantityToStrings = exports.validatePriceQuantity = exports.numberToString = exports.calcPrecision = exports.retry = exports.Retry = exports.sleep = exports.FIAT_SYMBOLS = void 0;
const assert_1 = require("assert");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const bs58_1 = __importDefault(require("bs58"));
const web3_utils_1 = __importDefault(require("web3-utils"));
exports.FIAT_SYMBOLS = ['CAD', 'CHF', 'EUR', 'GBP', 'JPY', 'USD'];
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
exports.sleep = sleep;
/* eslint-disable @typescript-eslint/explicit-function-return-type */
// See https://stackoverflow.com/a/29837695/381712
// Decorator for function is not supported in TypeScript,
// see https://github.com/microsoft/TypeScript/issues/7318
function Retry(times = 1, logger = console) {
assert_1.strict.ok(times > 0);
// eslint-disable-next-line func-names
return function (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: its value is never read
target,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: its value is never read
propertyName, propertyDesciptor) {
const originalMethod = propertyDesciptor.value;
// eslint-disable-next-line no-param-reassign,func-names
propertyDesciptor.value = async function (...args) {
let error = new Error();
try {
for (let i = 0; i < times; i += 1) {
// eslint-disable-next-line no-await-in-loop
return await originalMethod.apply(this, args);
}
}
catch (e) {
error = e;
logger.error(e);
}
throw error;
};
return propertyDesciptor;
};
}
exports.Retry = Retry;
async function retry(func, // eslint-disable-line no-shadow
times = 1, logger = console, ...args) {
assert_1.strict.ok(times > 0);
let error = new Error();
try {
for (let i = 0; i < times; i += 1) {
// eslint-disable-next-line no-await-in-loop
return await func(args);
}
}
catch (e) {
error = e;
logger.error(e);
}
throw error;
}
exports.retry = retry;
function calcPrecision(numberStr) {
if (!numberStr.includes('.'))
return 0;
return numberStr.length - numberStr.indexOf('.') - 1;
}
exports.calcPrecision = calcPrecision;
function numberToString(n, decimal, ceil = false) {
const rounded = new bignumber_js_1.default(n)
.times(new bignumber_js_1.default(10).pow(decimal + 1))
.integerValue()
.div(10);
const restored = ceil
? rounded.integerValue(bignumber_js_1.default.ROUND_CEIL)
: rounded.integerValue(bignumber_js_1.default.ROUND_DOWN);
return restored.div(new bignumber_js_1.default(10).pow(decimal)).toNumber().toFixed(decimal);
}
exports.numberToString = numberToString;
function validatePriceQuantity(market, price, quantity) {
assert_1.strict.equal(calcPrecision(price), market.precision.price, `${market.exchange} ${market.pair} precision.price doesn't match`);
assert_1.strict.equal(calcPrecision(quantity), market.precision.base, `${market.exchange} ${market.pair} precision.base doesn't match`);
// At least one of them exist
assert_1.strict.ok(market.minQuantity.base || market.minQuantity.quote);
if (market.minQuantity.base && parseFloat(quantity) < market.minQuantity.base) {
throw Error(`The base quantity ${quantity} is less than minQuantity.base ${market.minQuantity.base} ${market.base}`);
}
const quoteQuantity = parseFloat(price) * parseFloat(quantity);
if (market.minQuantity.quote && quoteQuantity <= market.minQuantity.quote) {
throw Error(`The order volume ${quoteQuantity} is less than minQuantity.quote ${market.minQuantity
.quote} ${market.quote}`);
}
return true;
}
exports.validatePriceQuantity = validatePriceQuantity;
function convertPriceAndQuantityToStrings(market, price, quantity, sell) {
const priceStr = numberToString(price, market.precision.price, !sell);
const quantityStr = numberToString(quantity, market.precision.base, false);
assert_1.strict.ok(validatePriceQuantity(market, priceStr, quantityStr));
return [priceStr, quantityStr];
}
exports.convertPriceAndQuantityToStrings = convertPriceAndQuantityToStrings;
function calcTokenPlatform(depositAddresses) {
const result = {};
Object.keys(depositAddresses).forEach((symbol) => {
const platforms = Object.keys(depositAddresses[symbol]);
assert_1.strict.equal(platforms.length, 1);
result[symbol] = platforms[0]; // eslint-disable-line prefer-destructuring
});
return result;
}
exports.calcTokenPlatform = calcTokenPlatform;
function detectPlatformFromAddress(address) {
if (address.indexOf('bc1') === 0)
return 'BTC';
try {
const hexString = bs58_1.default.decode(address).toString('hex');
if (hexString.length === 50) {
const prefixPlatformMap = {
'00': 'OMNI',
'05': 'OMNI',
'1e': 'DOGE',
'21': 'ELA',
'24': 'GRS',
'30': 'LTC',
'38': 'PAI',
'3c': 'KMD',
'41': 'TRC20',
'3a': 'QTUM',
'17': 'NEP5',
'49': 'WICC',
'4c': 'DASH',
'52': 'XZC',
'7c': 'XRP',
};
const prefix = hexString.slice(0, 2);
if (prefix in prefixPlatformMap)
return prefixPlatformMap[prefix];
}
else if (hexString.length === 52) {
const prefixPlatformMap = {
'01': 'WAVES',
'05': 'VSYS',
'07': 'DCR',
'09': 'HC',
'1c': 'ZEC',
'19': 'NRC20',
'20': 'ZEN',
};
const prefix = hexString.slice(0, 2);
if (prefix in prefixPlatformMap)
return prefixPlatformMap[prefix];
}
else if (hexString.length === 54) {
const prefixPlatformMap = {
'06': 'XTZ',
'9e': 'NULS',
};
const prefix = hexString.slice(0, 2);
if (prefix in prefixPlatformMap)
return prefixPlatformMap[prefix];
if (hexString.indexOf('06') === 0) {
return 'XTZ';
}
}
else if (hexString.length === 58) {
if (hexString.indexOf('08') === 0) {
return 'NEW';
}
}
else if (hexString.length === 60) {
if (hexString.indexOf('01') === 0) {
return 'XEM';
}
}
else if (hexString.length === 140) {
if (hexString.indexOf('02') === 0) {
return 'XMR';
}
}
else if (hexString.length === 144) {
if (hexString.indexOf('2c') === 0) {
return 'ETN';
}
}
else if (hexString.length === 152) {
if (hexString.indexOf('82') === 0) {
return 'ADA';
}
}
}
catch (e) {
// do nothing;
}
if (web3_utils_1.default.isAddress(address))
return 'ERC20';
if (address.indexOf('cosmos') === 0)
return 'ATOM';
if (address.indexOf('bnb') === 0)
return 'BEP2';
if (address.indexOf('zil') === 0)
return 'ZIL';
if (address.indexOf('hx') === 0)
return 'ICX';
if (address.indexOf('bm') === 0)
return 'BTM';
if (address.indexOf('ACT') === 0)
return 'ACT';
if (address.indexOf('ckb') === 0)
return 'CKB';
if (address.indexOf('ak_') === 0)
return 'AE';
if (address.indexOf('nano_') === 0)
return 'NANO';
if (/^[0-9]{1,20}L$/.test(address))
return 'LSK';
if (/^[0-9a-f]{76}$/.test(address))
return 'SC';
// https://github.com/EOSIO/eos/issues/955
if (/(^[a-z1-5.]{1,11}[a-z1-5]$)|(^[a-z1-5.]{12}[a-j1-5]$)/.test(address))
return 'EOS';
return undefined;
}
exports.detectPlatformFromAddress = detectPlatformFromAddress;
function detectPlatform(address, symbol) {
const platform = detectPlatformFromAddress(address);
if (platform === 'OMNI')
return ['BTC', 'BCH', 'BSV', 'BHD'].includes(symbol) ? symbol : platform;
if (platform === 'ERC20')
return ['ETH', 'ETC'].includes(symbol) ? symbol : platform;
if (platform === 'TRC20' && symbol === 'TRX')
return 'TRX';
if (platform === 'TRC20' && symbol === 'BTT')
return 'TRC10'; // BTT is a TRC10 token
if (platform === 'NRC20' && symbol === 'NAS')
return 'NAS';
if (platform === 'EOS' && symbol === 'EOS')
return 'EOS';
if (platform === 'BEP2' && symbol === 'BNB')
return 'BNB';
if (platform === 'NEP5' && symbol === 'NEO')
return 'NEO';
if (platform === 'DOGE')
return ['DOGE', 'XVG'].includes(symbol) ? symbol : platform;
return platform;
}
exports.detectPlatform = detectPlatform;