UNPKG

crypto-client

Version:

An unified client for all cryptocurrency exchanges.

279 lines (278 loc) 10.6 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.getWithdrawalFees = exports.fetchWithdrawInfo = exports.getDepositAddresses = exports.getDepositAddress = exports.queryAllBalances = exports.queryOrder = exports.cancelOrder = exports.placeOrder = void 0; const assert_1 = __importDefault(require("assert")); const axios_1 = __importDefault(require("axios")); const crypto_1 = __importDefault(require("crypto")); const crypto_pair_1 = require("crypto-pair"); const kraken_withdrawal_fee_1 = require("kraken-withdrawal-fee"); const qs_1 = __importDefault(require("qs")); const config_1 = require("../config"); const util_1 = require("../util"); const API_ENDPOINT = 'https://api.kraken.com'; function generateNonce() { return Date.now() * 1000; } function getSignature(path, params, privateKey) { const message = qs_1.default.stringify(params); const decryptedKey = Buffer.from(privateKey, 'base64'); const hashDigest = crypto_1.default .createHash('sha256') .update(params.nonce + message) .digest('latin1'); const hmacDigest = crypto_1.default .createHmac('sha512', decryptedKey) .update(path + hashDigest, 'latin1') .digest('base64'); return hmacDigest; } async function privateMethod(path, params) { assert_1.default.ok(config_1.USER_CONFIG.KRAKEN_API_KEY); assert_1.default.ok(config_1.USER_CONFIG.KRAKEN_PRIVATE_KEY); assert_1.default.ok(params.nonce); const url = `${API_ENDPOINT}${path}`; const signature = getSignature(path, params, config_1.USER_CONFIG.KRAKEN_PRIVATE_KEY); const headers = { 'API-Key': config_1.USER_CONFIG.KRAKEN_API_KEY, 'API-Sign': signature, }; const response = await axios_1.default.post(url, qs_1.default.stringify(params), { headers }).catch((e) => { return e; }); if (response instanceof Error) return response; assert_1.default.equal(response.status, 200); if (response.data.error.length > 0) { return new Error(response.data.error.join('\n')); } return response.data.result; } async function placeOrder(market, price, quantity, sell, clientOrderId) { try { assert_1.default.ok(market); assert_1.default.ok(config_1.USER_CONFIG.KRAKEN_PRIVATE_KEY); assert_1.default.ok(config_1.USER_CONFIG.KRAKEN_PRIVATE_KEY); const [priceStr, quantityStr] = util_1.convertPriceAndQuantityToStrings(market, price, quantity, sell); assert_1.default.ok(priceStr); assert_1.default.ok(quantityStr); const path = '/0/private/AddOrder'; const params = { pair: market.id, type: sell ? 'sell' : 'buy', ordertype: 'limit', price, volume: quantity, nonce: generateNonce(), }; if (clientOrderId) { params.userref = parseInt(clientOrderId, 10); } const data = await privateMethod(path, params); if (data instanceof Error) return data; assert_1.default.ok(data.txid.length > 0); return data.txid[0]; // TODO: handle txid.length > 1 } catch (e) { return e; } } exports.placeOrder = placeOrder; async function cancelOrder(orderId, clientOrderId) { const path = '/0/private/CancelOrder'; const params = { txid: orderId, nonce: generateNonce(), }; if (clientOrderId) { params.txid = parseInt(clientOrderId, 10); } const data = await privateMethod(path, params); if (data instanceof Error) return false; return data.count > 0; } exports.cancelOrder = cancelOrder; async function queryOrder(orderId, clientOrderId) { const path = '/0/private/QueryOrders'; const params = { txid: orderId, nonce: generateNonce(), }; if (clientOrderId) { params.userref = parseInt(clientOrderId, 10); } const data = await privateMethod(path, params); if (data instanceof Error) return undefined; return data[orderId]; } exports.queryOrder = queryOrder; async function queryAllBalances(all = false) { const path = '/0/private/BalanceEx'; const balances = await privateMethod(path, { nonce: generateNonce(), }); if (balances instanceof Error) return balances; const result = {}; Object.keys(balances).forEach((symbol) => { const symbolNormalized = crypto_pair_1.normalizeSymbol(symbol, 'Kraken'); result[symbolNormalized] = all ? parseFloat(balances[symbol].balance) : parseFloat(balances[symbol].balance) - parseFloat(balances[symbol].hold_trade); }); return result; } exports.queryAllBalances = queryAllBalances; async function getDepositMethod(symbol) { assert_1.default.ok(symbol); assert_1.default.ok(!util_1.FIAT_SYMBOLS.includes(symbol), `Fiat currency ${symbol} is not supported, only cryptocurrencies are supported`); if (symbol === 'DOGE') symbol = 'XDG'; // eslint-disable-line no-param-reassign if (symbol === 'USDT') { // see https://support.kraken.com/hc/en-us/requests/2732113 return { method: 'Tether USD', limit: false, fee: '0.00000000', 'gen-address': true, }; } const path = '/0/private/DepositMethods'; const params = { asset: symbol, nonce: generateNonce(), }; const arr = await privateMethod(path, params); if (arr instanceof Error) return arr; assert_1.default.equal(arr.length, 1); return arr[0]; } async function getDepositAddress(symbol, generateNew = false) { assert_1.default.ok(symbol); assert_1.default.ok(!util_1.FIAT_SYMBOLS.includes(symbol), `Fiat currency ${symbol} is not supported, only cryptocurrencies are supported`); const depositMethod = await getDepositMethod(symbol); if (depositMethod instanceof Error) return depositMethod; const path = '/0/private/DepositAddresses'; const params = { asset: symbol === 'DOGE' ? 'XDG' : symbol, method: depositMethod.method, nonce: generateNonce(), }; if (generateNew) params.new = true; const arr = await privateMethod(path, params); if (arr instanceof Error) return arr; if (arr.length <= 0) return new Error('Returned empty array'); const address = arr[arr.length - 1]; // prefer the oldest address const platform = util_1.detectPlatform(address.address, symbol) || symbol; const result = { symbol, platform, address: address.address, }; if (address.memo) result.memo = address.memo; if (parseFloat(depositMethod.fee) > 0) result.fee = parseFloat(depositMethod.fee); if (typeof depositMethod.limit === 'string') result.max_deposit_amount = parseFloat(depositMethod.limit); return result; } exports.getDepositAddress = getDepositAddress; async function getDepositAddresses(symbols) { assert_1.default.ok(symbols.length); symbols = symbols.filter((symbol) => !util_1.FIAT_SYMBOLS.includes(symbol)); // eslint-disable-line no-param-reassign const result = {}; for (let i = 0; i < symbols.length; i += 1) { const symbol = symbols[i]; if (!(symbol in result)) result[symbol] = {}; const data = await getDepositAddress(symbol); // eslint-disable-line no-await-in-loop await util_1.sleep(3000); // eslint-disable-line no-await-in-loop if (!(data instanceof Error)) { result[symbol][data.platform] = data; } else { // generate new address const newAddress = await getDepositAddress(symbol, true); // eslint-disable-line no-await-in-loop await util_1.sleep(3000); // eslint-disable-line no-await-in-loop if (!(newAddress instanceof Error)) { result[symbol][newAddress.platform] = newAddress; } } } return result; } exports.getDepositAddresses = getDepositAddresses; async function fetchWithdrawInfo(symbol, key, amount) { const path = '/0/private/WithdrawInfo'; const params = { asset: symbol === 'DOGE' ? 'XDG' : symbol, nonce: generateNonce(), key, amount, }; const data = await privateMethod(path, params); if (data instanceof Error) return data; return { method: data.method, fee: parseFloat(data.fee) }; } exports.fetchWithdrawInfo = fetchWithdrawInfo; async function getWithdrawalFees() { const result = {}; const fees = kraken_withdrawal_fee_1.getAllWithdrawalFees(); Object.keys(fees).forEach((symbol) => { const platform = fees[symbol].platform || symbol; if (!(symbol in result)) result[symbol] = {}; result[symbol][platform] = { symbol, platform, fee: fees[symbol].fee, min: fees[symbol].min, }; }); return result; } exports.getWithdrawalFees = getWithdrawalFees; async function withdraw(symbol, platform, key, amount) { if (!key) { return new Error('Kraken withdraw requires a key'); } const withdrawInfo = await fetchWithdrawInfo(symbol, key, amount); if (withdrawInfo instanceof Error) return withdrawInfo; const withdrawalFee = kraken_withdrawal_fee_1.getWithdrawalFee(symbol); if (withdrawalFee === undefined) { return new Error(`${symbol} doesn't exist in package kraken-withdrawal-fee`); } if (Math.abs(withdrawInfo.fee - withdrawalFee.fee) > Number.EPSILON) { return new Error(`fee from package kraken-withdrawal-fee is not consistent with API /0/private/WithdrawInfo`); } if (amount < withdrawalFee.min) { return new Error(`amount ${amount} is less than min ${withdrawalFee.min}`); } if (withdrawalFee.platform && platform !== withdrawalFee.platform) { return new Error(`platform ${platform} is not identical with withdrawalFee.platform ${withdrawalFee.platform}`); } const params = { asset: symbol === 'DOGE' ? 'XDG' : symbol, nonce: generateNonce(), amount, key, }; const data = await privateMethod('/0/private/Withdraw', params); if (data instanceof Error) return data; return data.refid; } exports.withdraw = withdraw;