UNPKG

crypto-client

Version:

An unified client for all cryptocurrency exchanges.

319 lines (318 loc) 13.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withdraw = exports.fetchCurrencies = exports.getWithdrawalFees = exports.getDepositAddresses = exports.fetchCurrencyList = exports.queryAllBalances = exports.queryAccounts = exports.queryOrder = exports.cancelOrder = exports.placeOrder = void 0; const assert_1 = require("assert"); const axios_1 = __importDefault(require("axios")); const crypto_1 = __importDefault(require("crypto")); const crypto_pair_1 = require("crypto-pair"); const config_1 = require("../config"); const util_1 = require("../util"); const DOMAIN = 'api.huobi.pro'; const API_ENDPOINT = `https://${DOMAIN}`; function signRequest(method, requestPath, params = {}) { assert_1.strict.ok(config_1.USER_CONFIG.HUOBI_ACCESS_KEY); assert_1.strict.ok(config_1.USER_CONFIG.HUOBI_SECRET_KEY); /* eslint-disable no-param-reassign */ params.Timestamp = new Date().toISOString().replace(/\..+/, ''); // .getTime()+ Huobi.info.timeOffset; params.SignatureMethod = 'HmacSHA256'; params.SignatureVersion = '2'; params.AccessKeyId = config_1.USER_CONFIG.HUOBI_ACCESS_KEY; /* eslint-enable no-param-reassign */ const query = Object.keys(params) .sort() .reduce((a, k) => { a.push(`${k}=${encodeURIComponent(params[k])}`); return a; }, []) .join('&'); const source = `${method}\n${DOMAIN}\n${requestPath}\n${query}`; let signature = crypto_1.default .createHmac('sha256', config_1.USER_CONFIG.HUOBI_SECRET_KEY) .update(source) .digest('base64'); // digest('hex'); // set the HMAC hash header signature = encodeURIComponent(signature); return `${API_ENDPOINT}${requestPath}?${query}&Signature=${signature}`; } async function placeOrder(market, price, quantity, sell, clientOrderId) { try { assert_1.strict.ok(market); assert_1.strict.ok(config_1.USER_CONFIG.HUOBI_ACCESS_KEY); assert_1.strict.ok(config_1.USER_CONFIG.HUOBI_SECRET_KEY); assert_1.strict.ok(config_1.USER_CONFIG.HUOBI_ACCOUNT_ID); const [priceStr, quantityStr] = util_1.convertPriceAndQuantityToStrings(market, price, quantity, sell); const path = '/v1/order/orders/place'; const params = { 'account-id': config_1.USER_CONFIG.HUOBI_ACCOUNT_ID.toString(), amount: quantityStr, price: priceStr, symbol: market.id, type: sell ? 'sell-limit' : 'buy-limit', }; if (clientOrderId) { params['client-order-id'] = clientOrderId; } const fullUrl = signRequest('POST', path, params); const response = await axios_1.default.post(fullUrl, params, { headers: { 'Content-Type': 'application/json' }, }).catch((e) => { return e; }); if (response instanceof Error) return response; assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); return response.data.data; } catch (e) { return e; } } exports.placeOrder = placeOrder; async function cancelOrder(orderId) { const path = `/v1/order/orders/${orderId}/submitcancel`; const params = {}; const fullUrl = signRequest('POST', path, params); const response = await axios_1.default.post(fullUrl, params, { headers: { 'Content-Type': 'application/json' }, }); assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); return response.data.data === orderId; } exports.cancelOrder = cancelOrder; async function queryOrder(orderId) { const path = `/v1/order/orders/${orderId}`; const fullUrl = signRequest('GET', path); const response = await axios_1.default.get(fullUrl, { headers: { 'Content-Type': 'application/json' } }); assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); return response.data.data; } exports.queryOrder = queryOrder; async function queryAccounts() { const path = '/v1/account/accounts'; const fullUrl = signRequest('GET', path); const response = await axios_1.default.get(fullUrl, { headers: { 'Content-Type': 'application/json' } }); assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); return response.data.data; } exports.queryAccounts = queryAccounts; async function queryAllBalances(all = false) { const path = `/v1/account/accounts/${config_1.USER_CONFIG.HUOBI_ACCOUNT_ID}/balance`; const fullUrl = signRequest('GET', path); const response = await axios_1.default.get(fullUrl, { headers: { 'Content-Type': 'application/json' }, }).catch((e) => { return e; }); if (response instanceof Error) return response; assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); const data = response.data.data; assert_1.strict.equal(data.id, config_1.USER_CONFIG.HUOBI_ACCOUNT_ID); assert_1.strict.equal(data.type, 'spot'); assert_1.strict.equal(data.state, 'working'); const result = {}; data.list .filter((x) => !all && x.type === 'trade') .forEach((x) => { result[crypto_pair_1.normalizeSymbol(x.currency, 'Huobi')] = parseFloat(x.balance); }); return result; } exports.queryAllBalances = queryAllBalances; async function getReferenceCurrencies() { const path = '/v2/reference/currencies'; const response = await axios_1.default.get(`${API_ENDPOINT}${path}`, { headers: { 'Content-Type': 'application/json' }, }); assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.code, 200); return response.data.data; } async function fetchCurrencyList() { const path = '/v1/common/currencys'; const response = await axios_1.default.get(`${API_ENDPOINT}${path}`, { headers: { 'Content-Type': 'application/json' }, }).catch((e) => { return e; }); if (response instanceof Error) return response; assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.status, 'ok'); return response.data.data; } exports.fetchCurrencyList = fetchCurrencyList; async function getDepositAddresses() { const result = {}; const currencies = await getReferenceCurrencies(); const symbolChainMap = {}; currencies .filter((x) => x.chains.length > 0) .forEach((x) => { const symbol = crypto_pair_1.normalizeSymbol(x.currency, 'Huobi'); if (!(symbol in symbolChainMap)) symbolChainMap[symbol] = {}; x.chains.forEach((y) => { symbolChainMap[symbol][y.chain] = y; }); }); const currencyList = await fetchCurrencyList(); if (currencyList instanceof Error) return result; const path = '/v2/account/deposit/address'; const arr = []; for (let i = 0; i < currencyList.length; i += 1) { const currency = currencyList[i]; const fullUrl = signRequest('GET', path, { currency }); // eslint-disable-next-line no-await-in-loop const response = await axios_1.default.get(fullUrl, { headers: { 'Content-Type': 'application/json' } }); assert_1.strict.equal(response.status, 200); assert_1.strict.equal(response.data.code, 200); assert_1.strict.ok(Array.isArray(response.data.data)); await util_1.sleep(100); // eslint-disable-line no-await-in-loop const arrTmp = response.data.data; arr.push(...arrTmp); } arr.forEach((x) => { const symbol = crypto_pair_1.normalizeSymbol(x.currency, 'Huobi'); if (!(symbol in result)) result[symbol] = {}; if (symbolChainMap[symbol][x.chain].depositStatus === 'prohibited' || symbolChainMap[symbol][x.chain].withdrawStatus === 'prohibited') { return; } let platform = symbolChainMap[symbol][x.chain].baseChainProtocol || symbol; // special logic if (symbol === 'BCH' && x.chain === 'bcc') platform = 'BCH'; // for debug only if (!symbolChainMap[symbol][x.chain].baseChainProtocol) { const detected = util_1.detectPlatform(x.address, symbol); if (detected !== symbol && detected !== 'ERC20' && detected !== 'NEP5') { // console.info(x); // console.info(`${detectPlatform(x.address, symbol)}, ${symbol}`); } } result[symbol][platform] = { symbol, platform, address: x.address, }; if (x.addressTag) result[symbol][platform].memo = x.addressTag; }); return result; } exports.getDepositAddresses = getDepositAddresses; async function getWithdrawalFees() { const arr = await getReferenceCurrencies(); const result = {}; arr .filter((x) => x.chains.length > 0) .forEach((x) => { const symbol = crypto_pair_1.normalizeSymbol(x.currency, 'Huobi'); if (!(symbol in result)) result[symbol] = {}; x.chains.forEach((y) => { const platform = y.baseChainProtocol || symbol; result[symbol][platform] = { symbol, platform, fee: parseFloat(y.transactFeeWithdraw || y.minTransactFeeWithdraw || '0.0'), min: parseFloat(y.minWithdrawAmt), }; }); }); return result; } exports.getWithdrawalFees = getWithdrawalFees; async function fetchCurrencies() { const arr = await getReferenceCurrencies(); const result = {}; arr .filter((x) => x.chains.length > 0) .forEach((x) => { const active = x.instStatus === 'normal'; const symbol = crypto_pair_1.normalizeSymbol(x.currency, 'Huobi'); result[symbol] = { symbol, active, depositEnabled: x.chains .map((chain) => chain.depositStatus) .some((status) => status === 'allowed'), withdrawalEnabled: x.chains .map((chain) => chain.withdrawStatus) .some((status) => status === 'allowed'), }; result[symbol].active = result[symbol].active && result[symbol].depositEnabled && result[symbol].withdrawalEnabled; }); return result; } exports.fetchCurrencies = fetchCurrencies; async function getChainInfo() { const arr = await getReferenceCurrencies(); const result = {}; arr .filter((x) => x.chains.length > 0) .forEach((x) => { const symbol = crypto_pair_1.normalizeSymbol(x.currency, 'Huobi'); if (!(symbol in result)) result[symbol] = {}; x.chains.forEach((y) => { const platform = y.baseChainProtocol || symbol; result[symbol][platform] = { currency: x.currency, chain: y.chain, baseChain: y.baseChain, baseChainProtocol: y.baseChainProtocol, fee: y.transactFeeWithdraw || y.minTransactFeeWithdraw || '0.0', min: parseFloat(y.minWithdrawAmt), withdrawPrecision: y.withdrawPrecision, withdrawal_enabled: y.withdrawStatus === 'allowed', }; }); }); return result; } async function withdraw(symbol, address, // only supports existing addresses in your withdrawal address list amount, platform, memo) { const path = '/v1/dw/withdraw/api/create'; const chainInfoMap = await getChainInfo(); if (!(symbol in chainInfoMap)) { return new Error(`${symbol} is not in chainInfoMap`); } if (!(platform in chainInfoMap[symbol])) { return new Error(`${platform} is not in chainInfoMap[${symbol}]`); } const chainInfo = chainInfoMap[symbol][platform]; if (!chainInfo.withdrawal_enabled) return new Error(`Huobi ${symbol} withdrawal is disabled now`); if (amount < chainInfo.min) return new Error(`The withdrawal amount ${amount} is less than Huobi ${symbol} minWithdrawAmt ${chainInfo.min}`); const params = { address, currency: chainInfo.currency, amount: util_1.numberToString(amount, chainInfo.withdrawPrecision, false), fee: chainInfo.fee, chain: chainInfo.chain, }; if (memo) params['addr-tag'] = memo; const fullUrl = signRequest('POST', path, params); const response = await axios_1.default.post(fullUrl, params, { headers: { 'Content-Type': 'application/json' }, }); assert_1.strict.equal(response.status, 200); if (response.data.data === null) return new Error(JSON.stringify(response.data)); return response.data.data.toString(); } exports.withdraw = withdraw;