crypto-client
Version:
An unified client for all cryptocurrency exchanges.
319 lines (318 loc) • 13.1 kB
JavaScript
;
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;