crypto-client
Version:
An unified client for all cryptocurrency exchanges.
279 lines (278 loc) • 10.6 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.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;