UNPKG

crypto-client

Version:

An unified client for all cryptocurrency exchanges.

530 lines (529 loc) 20.9 kB
"use strict"; 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;