crypto-client
Version:
An unified client for all cryptocurrency exchanges.
530 lines (529 loc) • 20.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
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.queryBalance = exports.queryAllBalances = exports.queryOrder = exports.cancelOrder = exports.placeOrder = exports.createOrder = exports.init = exports.SUPPORTED_EXCHANGES = void 0;
const assert_1 = require("assert");
const crypto_markets_1 = __importDefault(require("crypto-markets"));
const eosjs_ecc_1 = require("eosjs-ecc");
const config_1 = require("./config");
const Binance = __importStar(require("./exchanges/binance"));
const Bitfinex = __importStar(require("./exchanges/bitfinex"));
const Bitstamp = __importStar(require("./exchanges/bitstamp"));
const CoinbasePro = __importStar(require("./exchanges/coinbase_pro"));
const Huobi = __importStar(require("./exchanges/huobi"));
const Kraken = __importStar(require("./exchanges/kraken"));
const MXC = __importStar(require("./exchanges/mxc"));
const Newdex = __importStar(require("./exchanges/newdex"));
const OKEx = __importStar(require("./exchanges/okex"));
const WhaleEx = __importStar(require("./exchanges/whaleex"));
const util_1 = require("./util");
__exportStar(require("./pojo"), exports);
exports.SUPPORTED_EXCHANGES = [
'Binance',
'Bitfinex',
'Bitstamp',
'CoinbasePro',
'Huobi',
'Kraken',
'MXC',
'Newdex',
'OKEx',
'WhaleEx',
];
// Spot: exchange -> marketType -> pair -> Market
// Other: exchange -> marketType -> rawPair -> Market
const EXCHANGE_MARKET_CACHE = {};
/**
* Initialize.
*
*/
async function init({ eosAccount = '', eosPrivateKey = '', ethPrivateKey = '', BINANCE_API_KEY = '', BINANCE_API_SECRET = '', BITFINEX_API_KEY = '', BITFINEX_API_SECRET = '', BITSTAMP_USER_ID = 0, BITSTAMP_API_KEY = '', BITSTAMP_API_SECRET = '', COINBASE_ACCESS_KEY = '', COINBASE_ACCESS_SECRET = '', COINBASE_ACCESS_PASSPHRASE = '', DFUSE_API_KEY = '', HUOBI_ACCESS_KEY = '', HUOBI_SECRET_KEY = '', HUOBI_ACCOUNT_ID = 0, KRAKEN_API_KEY = '', KRAKEN_PRIVATE_KEY = '', MXC_ACCESS_KEY = '', MXC_SECRET_KEY = '', OKEX_SPOT_API_KEY = '', OKEX_SPOT_API_SECRET = '', OKEX_SPOT_API_PASSPHRASE = '', OKEX_SPOT_FUND_PASSWORD = '', WHALEEX_API_KEY = '', WHALEEX_USER_ID = '', }) {
if (eosAccount) {
config_1.USER_CONFIG.eosAccount = eosAccount;
if (!eosjs_ecc_1.isValidPrivate(eosPrivateKey))
throw Error(`Invalid EOS private key: ${eosPrivateKey}`);
config_1.USER_CONFIG.eosPrivateKey = eosPrivateKey;
}
if (DFUSE_API_KEY) {
config_1.USER_CONFIG.DFUSE_API_KEY = DFUSE_API_KEY;
}
if (ethPrivateKey)
config_1.USER_CONFIG.ethPrivateKey = ethPrivateKey;
if (BINANCE_API_KEY) {
assert_1.strict.ok(BINANCE_API_SECRET);
config_1.USER_CONFIG.BINANCE_API_KEY = BINANCE_API_KEY;
config_1.USER_CONFIG.BINANCE_API_SECRET = BINANCE_API_SECRET;
}
if (BITFINEX_API_KEY) {
assert_1.strict.ok(BITFINEX_API_SECRET);
config_1.USER_CONFIG.BITFINEX_API_KEY = BITFINEX_API_KEY;
config_1.USER_CONFIG.BITFINEX_API_SECRET = BITFINEX_API_SECRET;
}
if (BITSTAMP_API_KEY) {
assert_1.strict.ok(BITSTAMP_API_SECRET);
config_1.USER_CONFIG.BITSTAMP_API_KEY = BITSTAMP_API_KEY;
config_1.USER_CONFIG.BITSTAMP_API_SECRET = BITSTAMP_API_SECRET;
config_1.USER_CONFIG.BITSTAMP_USER_ID = BITSTAMP_USER_ID;
}
if (COINBASE_ACCESS_KEY) {
assert_1.strict.ok(COINBASE_ACCESS_SECRET);
assert_1.strict.ok(COINBASE_ACCESS_PASSPHRASE);
config_1.USER_CONFIG.COINBASE_ACCESS_KEY = COINBASE_ACCESS_KEY;
config_1.USER_CONFIG.COINBASE_ACCESS_SECRET = COINBASE_ACCESS_SECRET;
config_1.USER_CONFIG.COINBASE_ACCESS_PASSPHRASE = COINBASE_ACCESS_PASSPHRASE;
}
if (HUOBI_ACCESS_KEY) {
assert_1.strict.ok(HUOBI_SECRET_KEY);
config_1.USER_CONFIG.HUOBI_ACCESS_KEY = HUOBI_ACCESS_KEY;
config_1.USER_CONFIG.HUOBI_SECRET_KEY = HUOBI_SECRET_KEY;
config_1.USER_CONFIG.HUOBI_ACCOUNT_ID =
HUOBI_ACCOUNT_ID || (await Huobi.queryAccounts()).filter((x) => x.type === 'spot')[0].id;
}
if (KRAKEN_API_KEY) {
assert_1.strict.ok(KRAKEN_PRIVATE_KEY);
config_1.USER_CONFIG.KRAKEN_API_KEY = KRAKEN_API_KEY;
config_1.USER_CONFIG.KRAKEN_PRIVATE_KEY = KRAKEN_PRIVATE_KEY;
}
if (MXC_ACCESS_KEY) {
assert_1.strict.ok(MXC_ACCESS_KEY);
config_1.USER_CONFIG.MXC_ACCESS_KEY = MXC_ACCESS_KEY;
config_1.USER_CONFIG.MXC_SECRET_KEY = MXC_SECRET_KEY;
}
if (OKEX_SPOT_API_KEY) {
assert_1.strict.ok(OKEX_SPOT_API_SECRET);
assert_1.strict.ok(OKEX_SPOT_API_PASSPHRASE);
config_1.USER_CONFIG.OKEX_SPOT_API_KEY = OKEX_SPOT_API_KEY;
config_1.USER_CONFIG.OKEX_SPOT_API_SECRET = OKEX_SPOT_API_SECRET;
config_1.USER_CONFIG.OKEX_SPOT_API_PASSPHRASE = OKEX_SPOT_API_PASSPHRASE;
if (OKEX_SPOT_FUND_PASSWORD)
config_1.USER_CONFIG.OKEX_SPOT_FUND_PASSWORD = OKEX_SPOT_FUND_PASSWORD;
}
if (WHALEEX_API_KEY) {
assert_1.strict.ok(WHALEEX_USER_ID, 'WHALEEX_USER_ID is empty');
await WhaleEx.initilize(WHALEEX_API_KEY, WHALEEX_USER_ID);
}
}
exports.init = init;
async function getExchangeMarketAndUpdateCache(market) {
if (!(market.exchange in EXCHANGE_MARKET_CACHE) ||
!(market.type in EXCHANGE_MARKET_CACHE[market.exchange])) {
if (!(market.exchange in EXCHANGE_MARKET_CACHE))
EXCHANGE_MARKET_CACHE[market.exchange] = {};
if (!(market.type in EXCHANGE_MARKET_CACHE[market.exchange]))
EXCHANGE_MARKET_CACHE[market.exchange][market.type] = {};
}
if (!('fees' in market)) {
const markets = await crypto_markets_1.default(market.exchange, market.type);
markets.forEach((m) => {
const key = m.type === 'Spot' ? m.pair : m.id;
EXCHANGE_MARKET_CACHE[m.exchange][m.type][key] = m;
});
}
else {
EXCHANGE_MARKET_CACHE[market.exchange][market.type][market.type === 'Spot' ? market.pair : market.id] = market;
}
return EXCHANGE_MARKET_CACHE[market.exchange][market.type][market.type === 'Spot' ? market.pair : market.id];
}
/**
* Create an Order object but don't sent it.
*
* This API is only used in DEX exchanges.
*
* @param market The trading market
* @param price The price
* @param quantity The quantity
* @param sell true if sell, otherwise false
* @returns ActionExtended
*/
async function createOrder(market, price, quantity, sell) {
const realMarket = await getExchangeMarketAndUpdateCache(market);
assert_1.strict.ok(realMarket, `Can NOT find market for ${market.exchange} ${market.pair} in ${market.type} type`);
switch (market.exchange) {
case 'Newdex':
return Newdex.createOrder(realMarket, price, quantity, sell);
default:
throw Error(`Unknown exchange: ${market.exchange}`);
}
}
exports.createOrder = createOrder;
/**
* Place an order.
*
* @param market The trading market
* @param price The price
* @param quantity The quantity
* @param sell true if sell, otherwise false
* @returns transaction_id for dex, or order_id for central
*/
async function placeOrder(market, price, quantity, sell, clientOrderId) {
const realMarket = await getExchangeMarketAndUpdateCache(market);
assert_1.strict.ok(realMarket, `Can NOT find market for ${market.exchange} ${market.pair} in ${market.type} type`);
let result;
switch (market.exchange) {
case 'Binance':
result = await Binance.placeOrder(realMarket, price, quantity, sell);
break;
case 'Bitfinex':
result = await Bitfinex.placeOrder(realMarket, price, quantity, sell, clientOrderId);
break;
case 'Bitstamp':
result = await Bitstamp.placeOrder(realMarket, price, quantity, sell);
break;
case 'CoinbasePro':
result = await CoinbasePro.placeOrder(realMarket, price, quantity, sell);
break;
case 'Huobi':
result = await Huobi.placeOrder(realMarket, price, quantity, sell, clientOrderId);
break;
case 'Kraken':
result = await Kraken.placeOrder(realMarket, price, quantity, sell, clientOrderId);
break;
case 'MXC':
result = await MXC.placeOrder(realMarket, price, quantity, sell);
break;
case 'Newdex':
result = await Newdex.placeOrder(realMarket, price, quantity, sell);
break;
case 'OKEx':
result = await OKEx.placeOrder(realMarket, price, quantity, sell, clientOrderId);
break;
case 'WhaleEx': {
result = await WhaleEx.placeOrder(realMarket, price, quantity, sell);
break;
}
default:
throw Error(`Unknown exchange: ${market.exchange}`);
}
if (result instanceof Error)
throw result;
else
return result;
}
exports.placeOrder = placeOrder;
/**
* Cancel an order.
*
* @param market The trading market
* @param orderId Order ID
* @returns boolean if central, transaction_id if dex
*/
async function cancelOrder(market, orderId) {
assert_1.strict.ok(orderId);
const realMarket = await getExchangeMarketAndUpdateCache(market);
assert_1.strict.ok(realMarket, `Can NOT find market for ${market.exchange} ${market.pair} in ${market.type} type`);
switch (market.exchange) {
case 'Binance':
return Binance.cancelOrder(realMarket, orderId);
case 'Bitfinex':
return Bitfinex.cancelOrder(orderId);
case 'Bitstamp':
return Bitstamp.cancelOrder(orderId);
case 'CoinbasePro':
return CoinbasePro.cancelOrder(orderId);
case 'Huobi':
return Huobi.cancelOrder(orderId);
case 'Kraken':
return Kraken.cancelOrder(orderId);
case 'MXC':
return MXC.cancelOrder(realMarket, orderId);
case 'Newdex':
return Newdex.cancelOrder(orderId);
case 'OKEx':
return OKEx.cancelOrder(realMarket, orderId);
case 'WhaleEx':
return WhaleEx.cancelOrder(orderId);
default:
throw Error(`Unknown exchange: ${market.exchange}`);
}
}
exports.cancelOrder = cancelOrder;
/**
* Query an order.
*
* @param market The trading market
* @param orderId Order ID
* @returns The order information
*/
async function queryOrder(market, orderId) {
assert_1.strict.ok(orderId);
const realMarket = await getExchangeMarketAndUpdateCache(market);
assert_1.strict.ok(realMarket, `Can NOT find market for ${market.exchange} ${market.pair} in ${market.type} type`);
switch (market.exchange) {
case 'Binance':
return Binance.queryOrder(realMarket, orderId);
case 'Bitfinex':
return Bitfinex.queryOrder(orderId);
case 'Bitstamp':
return Bitstamp.queryOrder(orderId);
case 'CoinbasePro':
return CoinbasePro.queryOrder(orderId);
case 'Huobi':
return Huobi.queryOrder(orderId);
case 'Kraken':
return Kraken.queryOrder(orderId);
case 'MXC':
return MXC.queryOrder(realMarket, orderId);
case 'Newdex':
return Newdex.queryOrder(orderId);
case 'OKEx':
return OKEx.queryOrder(realMarket, orderId);
case 'WhaleEx':
return WhaleEx.queryOrder(orderId);
default:
throw Error(`Unknown exchange: ${market.exchange}`);
}
}
exports.queryOrder = queryOrder;
/**
* Get all balances of an exchange.
*
* @param exchange The exchange name
* @param all Only used for debugging. False, get only available balances; True, get all including free and locked balances. Default to false.
*/
async function queryAllBalances(exchange, all = false) {
let result;
switch (exchange) {
case 'Binance':
result = await Binance.queryAllBalances(all);
break;
case 'Bitfinex':
result = await Bitfinex.queryAllBalances(all);
break;
case 'Bitstamp':
result = await Bitstamp.queryAllBalances(all);
break;
case 'CoinbasePro':
result = await CoinbasePro.queryAllBalances(all);
break;
case 'Huobi':
result = await Huobi.queryAllBalances(all);
break;
case 'Kraken':
result = await Kraken.queryAllBalances();
break;
case 'MXC':
result = await MXC.queryAllBalances(all);
break;
case 'Newdex':
result = await Newdex.queryAllBalances();
break;
case 'OKEx':
result = await OKEx.queryAllBalances(all);
break;
case 'WhaleEx':
result = await WhaleEx.queryAllBalances(all);
break;
default:
throw Error(`Unknown exchange: ${exchange}`);
}
if (result instanceof Error)
return result;
// filter out zero balances
const resultTmp = result;
Object.keys(resultTmp).forEach((symbol) => {
if (resultTmp[symbol] <= 0)
delete resultTmp[symbol];
});
return resultTmp;
}
exports.queryAllBalances = queryAllBalances;
async function queryBalance(exchange, symbol) {
if (exchange === 'Newdex')
return Newdex.queryBalance(symbol);
const balances = await queryAllBalances(exchange);
if (balances instanceof Error)
return 0;
return balances[symbol] || 0;
}
exports.queryBalance = queryBalance;
/**
* Get deposit addresses.
*
* @param exchangeName The exchange name
* @params symbols Symbols to retreive
* @returns symbol->platform->DepositAddress
*/
async function getDepositAddresses(exchange, symbols) {
assert_1.strict.ok(symbols);
if (symbols.length === 0)
return {};
switch (exchange) {
case 'Binance':
return Binance.getDepositAddresses(symbols);
case 'Bitfinex':
return Bitfinex.getDepositAddresses(symbols);
case 'Bitstamp':
return Bitstamp.getDepositAddresses(symbols);
case 'CoinbasePro':
return CoinbasePro.getDepositAddresses(symbols);
case 'Huobi':
return Huobi.getDepositAddresses();
case 'Kraken':
return Kraken.getDepositAddresses(symbols);
case 'OKEx':
return OKEx.getDepositAddresses(symbols);
case 'Newdex':
return Newdex.getDepositAddresses(symbols);
case 'WhaleEx':
return WhaleEx.getDepositAddresses(symbols);
default:
throw Error(`Unsupported exchange: ${exchange}`);
}
}
exports.getDepositAddresses = getDepositAddresses;
/**
*
* @param exchangeName The exchange name
* @params symbols Symbols to retreive
* @returns symbol->platform -> WithdrawalFee
*/
async function getWithdrawalFees(exchange, symbols) {
assert_1.strict.ok(exchange);
if (symbols)
assert_1.strict.ok(symbols.length, 'symbols is an empty array');
switch (exchange) {
case 'Binance':
return Binance.getWithdrawalFees();
case 'Bitfinex':
return Bitfinex.getWithdrawalFees();
case 'Bitstamp':
return Bitstamp.getWithdrawalFees();
case 'CoinbasePro':
return CoinbasePro.getWithdrawalFees();
case 'Huobi':
return Huobi.getWithdrawalFees();
case 'Kraken':
return Kraken.getWithdrawalFees();
case 'Newdex': {
if (symbols === undefined || symbols.length <= 0)
throw Error(`${exchange} requires an array of symbols`);
return Newdex.getWithdrawalFees(symbols);
}
case 'OKEx':
return OKEx.getWithdrawalFees();
case 'WhaleEx': {
if (symbols === undefined || symbols.length <= 0)
throw Error(`${exchange} requires an array of symbols`);
return WhaleEx.getWithdrawalFees(symbols);
}
default:
throw Error(`Unsupported exchange: ${exchange}`);
}
}
exports.getWithdrawalFees = getWithdrawalFees;
/**
* Fetch deposit and withdrawal statuses.
*
* Similar to fetchCurrencies() of ccxt.
*
* @param exchange The exchange name
* @returns symbol -> chain -> SymbolStatus or symbol -> SymbolStatus
*/
async function fetchCurrencies(exchange) {
assert_1.strict.ok(exchange);
switch (exchange) {
case 'Binance':
return Binance.fetchCurrencies();
case 'Huobi':
return Huobi.fetchCurrencies();
case 'OKEx':
return OKEx.fetchCurrencies();
default:
throw Error(`Unsupported exchange: ${exchange}`);
}
}
exports.fetchCurrencies = fetchCurrencies;
/**
* Withdraw.
*
* @param exchange The exchange name
* @param symbol The currency symbol
* @param address Destination address
* @param amount Withdrawal amount
* @param memo Optional, some currencies like EOS require addtional memo
* @param params Additional parameters, each exchange is different
* @returns Withdrawal ID
*/
async function withdraw(exchange, symbol, address, amount, memo, params = {}) {
assert_1.strict.ok(exchange);
const platform = util_1.detectPlatform(address, symbol);
if (platform === undefined)
throw new Error(`Failed to detect platform for address ${address} of ${symbol}`);
const symbolsRequirePlatform = ['USDT'];
if (symbolsRequirePlatform.includes(symbol) && !platform) {
throw new Error(`Failed to detect platform of ${symbol}`);
}
const symbolsRequireMemoList = ['ATOM', 'EOS', 'XLM', 'XRP'];
if (!memo) {
if (symbolsRequireMemoList.includes(symbol))
throw new Error(`${symbol} requires memo`);
if (symbolsRequireMemoList.includes(platform))
throw new Error(`${symbol} on ${platform} requires memo`);
}
let result;
switch (exchange) {
case 'Binance':
result = await Binance.withdraw(symbol, address, amount, platform, memo);
break;
case 'Bitfinex':
result = await Bitfinex.withdraw(symbol, address, amount, platform, memo, params);
break;
case 'Bitstamp':
result = await Bitstamp.withdraw(symbol, address, amount, memo);
break;
case 'CoinbasePro':
result = await CoinbasePro.withdraw(symbol, address, amount, memo);
break;
case 'Huobi':
result = await Huobi.withdraw(symbol, address, amount, platform, memo);
break;
case 'Kraken': {
if (params.key === undefined) {
throw new Error('Kraken withdraw requires a key');
}
result = await Kraken.withdraw(symbol, platform, params.key, amount);
break;
}
case 'MXC':
throw Error(`MXC does NOT have withdraw API`);
case 'Newdex':
result = await Newdex.withdraw(symbol, address, amount, platform, memo);
break;
case 'OKEx':
result = await OKEx.withdraw(symbol, address, amount, platform, memo);
break;
case 'WhaleEx':
throw Error(`WhaleEx does NOT have withdraw API`);
default:
throw Error(`Unsupported exchange: ${exchange}`);
}
if (result instanceof Error)
throw result;
else
return result;
}
exports.withdraw = withdraw;