UNPKG

crypto-client

Version:

An unified client for all cryptocurrency exchanges.

252 lines (251 loc) 9.65 kB
"use strict"; 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;